Calling FirstOrDefault multiple times in LINQ to set object properties - c#

I have a LINQ query like so:
var Item = (from s in contextD.Items where s.Id == Id select s).ToList();
Further down the Item object properties are set like so:
Item.FirstOrDefault().Qty = qtyToUpdate;
Item.FirstOrDefault().TotalPrice = UItem.FirstOrDefault().Qty * UItem.FirstOrDefault().Price;
...
My question is, will calling FirstOrDefault always loop through the result set returned by the query?
Shouldn't a single call be made and put in an object like so:
MyObject objMyObject = new MyObject;
objMyObject = Item.FirstOrDefault();
and then go about setting objMyObject properties.
The first part using FirstOrDefault is actually in the production, I am trying to find out if that's the right way.
Regards.

will calling FirstOrDefault always loop through the result set
returned by the query?
FirstOrDefault() never loops through all result set - it either returns first item, or default if set is empty. Also in this particular case even enumerator will not be created - thus you are calling FirstOrDefault() on variable of List<T> type, simply item at index 0 will be returned (if list is not empty). If you will investigate Enumerable.FirstOrDefault() implementation:
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
if (list.Count > 0)
{
return list[0];
}
}
But this will be invoked each time you are calling FirstOrDefault().
Also you are missing 'default' case. If you are chaining methods like in your first sample, you can get NullReferenceException if list is empty. So, make sure something was returned by query:
var item = Item.FirstOrDefault();
if (item == null)
return; // or throw
item.Qty = qtyToUpdate;
var uitem = UItem.FirstOrDefault();
if (uitem == null)
return; // or throw
item.TotalPrice = uitem.Qty * uitem.Price;
One more note - you have little difference in performance if you are performing FirstOrDefault() on in-memory collection. But difference will be huge if you will perform it without saving query results into list. In that case each FirstOrDefault() call will cause new database query.

Related

Issue with query returning null before I can check for null

For an invite function, I have a screen with a single field on it that lets users enter a code they've been given elsewhere. I then check in the controller to see which record is associated to this code. But my issue is with the controller blowing up if the code is not the first type is checks for (type 1 or type 2). So here what I believe it should be like(this doesn't work), although I realize the sloppiness I'm just new to c#.
Record rec = db.Records.Where(u = u.Code1).First();
If (rec != null)
{
Other code...
Exit controller
}
Record rec2 = db.Records.Where(u = u.Code2).First();
If (rec2 != null)
{
Other code...
Exit controller
}
Return to view - codes are invalid.
I've attempted other versions where I put the object check within the if statement but there didn't work either. Any ideas?
Use FirstOrDefault not First. First throws an exception if the collection is empty, FirstOrDefault returns default(T) if the collection is empty.
And actually, there are overloads of First and FirstOrDefault that take a condition parameter, so you don't need to call Where
ex:
var x = numbers.FirstOrDefault(n => n % 2 == 1);
This will return the first odd number in the collection, or 0 if there are none that meet the condition (or none at all.)
As explained by the MSDN docs on IEnumerable.First, this method throws an exception if there are no elements in the sequence
The First(IEnumerable) method throws an exception if
source contains no elements. To instead return a default value when
the source sequence is empty, use the FirstOrDefault method
So you should write
Record rec = db.Records.FirstOrDefault(u => u.Code1);
if(rec == null)
{
....
}
Notice that the same condition used in the Where extension could be used directly with FirstOrDefault
By the way, it is unclear what your expression is. The lambda expression (syntax is => ) should return a boolean value so, or your Code1 is a boolean variable or something is wrong in that line
Use db.Records.Where(u => u.Code1).FirstOrDefault();
Check the comparison value (ex. u.code1) if it has this property from the model before binding to the query.

Lambda loop thru ICollection failed to find object property

I have a list of objects as ICollection<objectA> listA. Now I'm trying to loop thru this listA and trying to match a condition and assign the result found into a variable. I tried below:
varB.someDesc = listA.FirstOrDefault(x=>x.ID == varB.ID).someDesc
Error complaining that x.ID is object null reference. When I put a break point, I can see there are many items in listA and there is an attribute ID. May I know what goes wrong?
I suggest validating the return value of FirstOrDefault() as follows:
var item = listA.FirstOrDefault(x=>x.ID == varB.ID);
if (item != null)
varB.someDesc = item.someDesc;
The error might not be exactly what you think it is.
Try this code.
varB.someDesc = listA.Where(x=>x.ID == varB.ID).FirstOrDefault().someDesc
you better check Object Null before assigning.

IEnumerable<T>.Union(IEnumerable<T>) overwrites contents instead of unioning

I've got a collection of items (ADO.NET Entity Framework), and need to return a subset as search results based on a couple different criteria. Unfortunately, the criteria overlap in such a way that I can't just take the collection Where the criteria are met (or drop Where the criteria are not met), since this would leave out or duplicate valid items that should be returned.
I decided I would do each check individually, and combine the results. I considered using AddRange, but that would result in duplicates in the results list (and my understanding is it would enumerate the collection every time - am I correct/mistaken here?). I realized Union does not insert duplicates, and defers enumeration until necessary (again, is this understanding correct?).
The search is written as follows:
IEnumerable<MyClass> Results = Enumerable.Empty<MyClass>();
IEnumerable<MyClass> Potential = db.MyClasses.Where(x => x.Y); //Precondition
int parsed_key;
//For each searchable value
foreach(var selected in SelectedValues1)
{
IEnumerable<MyClass> matched = Potential.Where(x => x.Value1 == selected);
Results = Results.Union(matched); //This is where the problem is
}
//Ellipsed....
foreach(var selected in SelectedValuesN) //Happens to be integer
{
if(!int.TryParse(selected, out parsed_id))
continue;
IEnumerable<MyClass> matched = Potential.Where(x => x.ValueN == parsed_id);
Results = Results.Union(matched); //This is where the problem is
}
It seems, however, that Results = Results.Union(matched) is working more like Results = matched. I've stepped through with some test data and a test search. The search asks for results where the first field is -1, 0, 1, or 3. This should return 4 results (two 0s, a 1 and a 3). The first iteration of the loops works as expected, with Results still being empty. The second iteration also works as expected, with Results containing two items. After the third iteration, however, Results contains only one item.
Have I just misunderstood how .Union works, or is there something else going on here?
Because of deferred execution, by the time you eventually consume Results, it is the union of many Where queries all of which are based on the last value of selected.
So you have
Results = Potential.Where(selected)
.Union(Potential.Where(selected))
.Union(potential.Where(selected))...
and all the selected values are the same.
You need to create a var currentSelected = selected inside your loop and pass that to the query. That way each value of selected will be captured individually and you won't have this problem.
You can do this much more simply:
Reuslts = SelectedValues.SelectMany(s => Potential.Where(x => x.Value == s));
(this may return duplicates)
Or
Results = Potential.Where(x => SelectedValues.Contains(x.Value));
As pointed out by others, your LINQ expression is a closure. This means your variable selected is captured by the LINQ expression in each iteration of your foreach-loop. The same variable is used in each iteration of the foreach, so it will end up having whatever the last value was. To get around this, you will need to declare a local variable within the foreach-loop, like so:
//For each searchable value
foreach(var selected in SelectedValues1)
{
var localSelected = selected;
Results = Results.Union(Potential.Where(x => x.Value1 == localSelected));
}
It is much shorter to just use .Contains():
Results = Results.Union(Potential.Where(x => SelectedValues1.Contains(x.Value1)));
Since you need to query multiple SelectedValues collections, you could put them all inside their own collection and iterate over that as well, although you'd need some way of matching the correct field/property on your objects.
You could possibly do this by storing your lists of selected values in a Dictionary with the name of the field/property as the key. You would use Reflection to look up the correct field and perform your check. You could then shorten the code to the following:
// Store each of your searchable lists here
Dictionary<string, IEnumerable<MyClass>> DictionaryOfSelectedValues = ...;
Type t = typeof(MyType);
// For each list of searchable values
foreach(var selectedValues in DictionaryOfSelectedValues) // Returns KeyValuePair<TKey, TValue>
{
// Try to get a property for this key
PropertyInfo prop = t.GetProperty(selectedValues.Key);
IEnumerable<MyClass> localSelected = selectedValues.Value;
if( prop != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(prop.GetValue(x, null))));
}
else // If it's not a property, check if the entry is for a field
{
FieldInfo field = t.GetField(selectedValues.Key);
if( field != null )
{
Results = Results.Union(Potential.Where(x =>
localSelected.Contains(field.GetValue(x, null))));
}
}
}
No, your use of union is absoloutely correct.
The only thing to keep in mind is it excludes duplicates as based on the equality operator. Do you have sample data?
Okay, I think you are are haveing a problem because Union uses deferred execution.
What happens if you do,
var unionResults = Results.Union(matched).ToList();
Results = unionResults;

Linq VAR and Typed Object

I would like a sample of code.
At the moment I use linq in c# and asp.net 4 ef4
var querySlotOrder = from slot in context.CmsSlots
where slot.SlotId == myCurrentSlotId
select slot;
if (querySlotOrder.SlotOrder == myNewSlotOrder)
e.Cancel = true;
This linq query return only a record.
Using VAR I cannot get the Typed Object and I cannot access its property SlotOrder.
How to change the query? thanks for your help
Usefully resource on the topic:
http://msdn.microsoft.com/en-us/library/bb384065.aspx
http://msdn.microsoft.com/en-us/library/bb397947.aspx
http://msdn.microsoft.com/en-us/library/bb397678.aspx
Even if your query returns a single object, the Select method, which you are using behind the scenes, doesn't. It returns an IQueryable<T> in EF.
You should use a method such as Single, SingleOrDefault, First, FirstOrDefault if you want to store a single object.
var querySlotOrder = (from slot in context.CmsSlots
where slot.SlotId == myCurrentSlotId
select slot).Single();
The difference between the four methods is:
Single: Returns the only element of a sequence, and throws an exception if there is not exactly one element in the sequence.
SingleOrDefault: Returns the only element of a sequence, or a default value if the sequence is empty; this method throws an exception if there is more than one element in the sequence.
First: Returns the first element of a sequence.
FirstOrDefault: Returns the first element of a sequence, or a default value if the sequence contains no elements.
(Definitions from MSDN)
The LINQ select-statement always returns a queryable collection. Therefore you need to fetch a single object from it.
var querySlotOrder = (from slot in context.CmsSlots
where slot.SlotId == myCurrentSlotId
select slot).FirstOrDefault();
The type of the returned object is IQueryable<CmdSlot> (assuming that CmdSlot is the type of the elements), and querySlotOrder gets that type (that's the effect of var; var itself is not a type). If you are absolutely sure that there will always be exactly one element in the result collection, you can retrieve it with querySlotOrder.Single().
The linq query returns not a record, but a collection of records that has only 1 element. If you want to get the first element and if you are sure that there is only 1 element in the collection, use Single extension method:
var querySlotOrders = from slot in context.CmsSlots
where slot.SlotId == myCurrentSlotId
select slot;
var querySlotOrder = querySlotOrders.Single();
if (querySlotOrder.SlotOrder == myNewSlotOrder)
e.Cancel = true;
As Dennis is pointing out in his answer, you're not getting an instance of one object, you're getting an IEnumerable back from your query. In order to access the SlotOrder property you need to pick a particular item from the collection, most likely the first - judging by your query.
This is madd0's code but reformated to use C# style instead of SQL style
var querySlotOrder = context.CmsSlots
.Where(slot => slot.SlotId == myCurrentSlotId)
.Single();
it's better to read and understand whan SQL style
Why do you use a var? if you know the type of the object you expect you should type querySlotOrder:
MyObjectType querySlotOrder = (from slot in context.CmsSlots
where slot.SlotId == myCurrentSlotId
select slot).FirstOrDefault();
if (querySlotOrder.SlotOrder == myNewSlotOrder)
e.Cancel = true;

Why does this linq extension method hit the database twice?

I have an extension method called ToListIfNotNullOrEmpty(), which is hitting the DB twice, instead of once. The first time it returns one result, the second time it returns all the correct results.
I'm pretty sure the first time it hits the database, is when the .Any() method is getting called.
here's the code.
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value.IsNullOrEmpty())
{
return null;
}
if (value is IList<T>)
{
return (value as IList<T>);
}
return new List<T>(value);
}
public static bool IsNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value != null)
{
return !value.Any();
}
return true;
}
I'm hoping to refactor it so that, before the .Any() method is called, it actually enumerates through the entire list.
If i do the following, only one DB call is made, because the list is already enumerated.
var pewPew = (from x in whatever
select x)
.ToList() // This enumerates.
.ToListIsNotNullOrEmpty(); // This checks the enumerated result.
I sorta don't really want to call ToList() then my extension method.
Any ideas, folks?
I confess that I see little point in this method. Surely if you simply do a ToList(), a check to see if the list is empty suffices as well. It's arguably harder to handle the null result when you expect a list because then you always have to check for null before you iterate over it.
I think that:
var query = (from ...).ToList();
if (query.Count == 0) {
...
}
works as well and is less burdensome than
var query = (from ...).ToListIfNotNullOrEmpty();
if (query == null) {
...
}
and you don't have to implement (and maintain) any code.
How about something like this?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if (value == null)
return null;
var list = value.ToList();
return (list.Count > 0) ? list : null;
}
To actually answer your question:
This method hits the database twice because the extension methods provided by the System.Linq.Enumerable class exhibit what is called deferred execution. Essentially, this is to eliminate the need for constructing a string of temporarily cached collections for every part of a query. To understand this, consider the following example:
var firstMaleTom = people
.Where(p => p.Gender = Gender.Male)
.Where(p => p.FirstName == "Tom")
.FirstOrDefault();
Without deferred execution, the above code might require that the entire collection people be enumerated over, populating a temporary buffer array with all the individuals whose Gender is Male. Then it would need to be enumerated over again, populating another buffer array with all of the individuals from the first buffer whose first name is Tom. After all that work, the last part would return the first item from the resulting array.
That's a lot of pointless work. The idea with deferred execution is that the above code really just sets up the firstMaleTom variable with the information it needs to return what's being requested with the minimal amount of work.
Now, there's a flip side to this: in the case of querying a database, deferred execution means that the database gets queried when the return value is evaluated. So, in your IsNullOrEmpty method, when you call Any, the value parameter is actually being evaluated right then and there -- hence a database query. After this, in your ToListIfNotNullOrEmpty method, the line return new List<T>(value) also evaluates the value parameter -- because it's enumerating over the values and adding them to the newly created List<T>.
You could stick the .ToList() call inside the extension, the effect is slightly different, but does this still work in the cases you have?
public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
if(value == null)
{
return null;
}
var result = value.ToList();
return result.IsNullOrEmpty() ? null : result;
}

Categories