Is this code good enough:
if (MyCollection.Where(p => p.SomeID == idstring).Any())
{
selectedval = MyCollection.Where(p => p.SomeID == idstring).FirstOrDefault().MyField;
}
My doubt is about the fact that I make the same query twice: first for null check and then to actually get data.
Maybe there is a better way to do this type of things?
Yes.
var item = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if (item != null)
selectval = item.MyField;
This avoids double querying the collection, which will certainly make a difference in big collections or if your collection performs a DB query.
There is. You can use the FirstOrDefault method which takes a predicate and returns null if the item is not found.
var result = MyCollection.FirstOrDefault(p => p.SomeID == idstring);
if( result != null )
{
// item was found
}
Related
I'm working on a LINQ statement. I have a table of cities where the records have either a countryId or a stateId. I'd like to just write the one statement and have the where clause check to see which of the two parameters is null and then select on the one that is not.
Here's what I'm working with:
public List<City> Cities(int? countryTypeId, int? stateTypeId)
{
if (countryTypeId == null && stateTypeId == null)
return null;
return _db.City
.Where(x => x.StateTypeId == stateTypeId
&& x.CountryTypeId == countryTypeId)
.OrderBy(x => x.Description)
.ToDTOs();
}
I'm pretty new to LINQ, and I know this code isn't right, just adding it for context.
If the State and Country ids are all >0 you simply can do this, no need to check for null
.Where(x => x.StateTypeId == stateTypeId.GetValueOrDefault()
&& x.CountryTypeId == countryTypeId.GetValueOrDefault())
Else you need to add the condition if those nullable inputs have value or not, as mentioned in the comment
Edit: after seeing some comments, if you are looking for list of cities based on either of the parameters, then you should be using || not && in your where condition
Where(x => (stateTypeId.HasValue && stateTypeId.Value == x.StateTypeId)
|| (countryTypeId.HasValue && countryTypeId.Value == x.CountryTypeId))
Note the order matters, this code will first check if stateTypeId has value and if it has it'll match only the cities with that stateTypeId
_db.City.Where(c => c.CountryTypeId?.Equals(countryTypeId) ?? false
| c.StateTypeId?.Equals(stateTypeId) ?? false);
Using null conditional operators - when a type Id is null use the null coalescing operator to return false and fail the match - otherwise check for equality and return matching.
Note you cannot short circuit the OR operator here!
I'm not sure if this is the case, but if one of the input parameters was always null and the entries were guaranteed to always have one property null, the following would be a cool solution:
_db.City.Where(c => (c.CountryTypeId ?? c.StateTypeId) == (countryTypeId ?? stateTypeId))
My DBA has sufficiently beaten it into my head that ignoring parameters in a query (ex: WHERE Field = #PARAM or #PARAM IS NULL) can result in very bad things. As a result, I would encourage you to conditionally add only the parameters that you absolutely need. Fortunately, given that you are working with just two possible parameters, this is trivial.
Start with the base of your query, and then add to it.
var queryBase = _db.City.OrderBy(x => x.Description);
if (countryTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.CountryTypeId == countryTypeId);
}
if (stateTypeId.HasValue)
{
queryBase = queryBase.Where(x => x.StateTypeId == stateTypeId);
}
return queryBase.ToDTOs(); // or .ToList() for a more universal outcome
Add whatever logic you may need if parameters are mutually exclusive, one supercedes the other, etc.
i am getting an error that says 'Non Static Method requires a target'
Here is the code that is causing me the error, could anyone possible shed some light on this?
//TODO: Error, Non static method requires a target.
var orderItem =
_context.PurchaseOrderItems.FirstOrDefault(
p => p.JobReference == item.JobReference && p.ItemNumber == item.ItemNumber);
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => d.PurchaseOrderItem.Id == orderItem.Id)
.ToList();
The FirstOrDefault method may return null value if no query results returned there:
var orderItem = _context.PurchaseOrderItems.FirstOrDefault(
p => p.JobReference == item.JobReference && p.ItemNumber == item.ItemNumber);
Since orderItem.Id throws NullReferenceException when orderItem is null, it will propagate to LINQ throwing TargetException as mentioned in question (see this post and this post for more info).
Hence, you need to check presence of null value from orderItem by modifying second LINQ query to this one:
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => (orderItem != null && d.PurchaseOrderItem.Id == orderItem.Id))
.ToList();
NB: Null checking must takes place before retrieving property Id of orderItem to prevent NullReferenceException.
As an alternative, if condition to check against null value may be used without modifying second query:
if (orderItem != null)
{
return _context.DeliverySchedules.Include(d => d.PurchaseOrderItem)
.Where(d => d.PurchaseOrderItem.Id == orderItem.Id)
.ToList();
}
Change the FirstOrDefault to Single because in the next line you will get to its properties and you dont want a NullReferenceException
I'm trying to write a lambda expression that looks at an array of objects ('fields') that may or may not have specific items in it. I would like to retrieve values if they exist; if no field in the array has a code of 'SomeCode', then there is no value to retrieve. I'm curious if there is a cleaner way to write the below, since I'll need to do this for a lot of fields and done want to run through the 'FirstOrDefault' call twice.
if (fields.FirstOrDefault(x => x.Code == "SomeCode") != null)
{
obj.CodeValue = fields.FirstOrDefault(x => x.Code == "SomeCode").Value;
}
EDIT: Thanks for any insight on doing this in a 'Lambda only' fashion; I'd like to improve my skills in this area and figured that there would be something cleaner than either calling it twice or simply assigning it to an interim object.
Just cache the return value from FirstOrDefault and then compare it.
var item = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (item != null)
{
obj.CodeValue = item.Value;
}
In your current code you are querying twice, once for checking null, later to access value.
Try this:
var field = fields.FirstOrDefault(x => x.Code == "SomeCode");
if (field != null)
obj.CodeValue = field.Value;
Is there a way to do the following using Linq:
foreach (var c in collection)
{
if (c.Condition == condition)
{
c.PropertyToSet = value;
// I must also check I only set this value to one minimum and only one element.
}
else
{
c.PropertyToSet = otherValue;
}
}
To clarify, I want to iterate through each object in a collection and then update a property on each object except for one element of my collection that should updated to another value.
At this moment I use a counter to check I set my value to one and only one element of my collection. I removed it from this example to let people suggest other solutions.
The original question without exception in collection is here
EDIT
I ask this question because I'm not sure it's possible to do it with LinQ. so your answers comfort my opinion about LinQ. Thank you.
You can use .ForEach to make the change, and .Single to verify only one element matches the condition:
// make sure only one item matches the condition
var singleOne = collection.Single(c => c.Condition == condition);
singleOne.PropertyToSet = value;
// update the rest of the items
var theRest = collection.Where(c => c.Condition != condition);
theRest.ToList().ForEach(c => c.PropertyToSet = otherValue);
I don't suggest you to implement this with Linq. Why? Because Linq is for querying, not for modification. It can return you objects which match some condition, or objects which don't match. But for updating those objects you still need to use foreach or convert query results to list and use ForEach extension. Both will require enumerating sequence twice.
So, simple loop will do the job:
foreach (var c in collection)
{
c.PropertyToSet = (c.Condition == condition) ? value : otherValue;
}
collection.Where(x => <condition>).ToList().ForEach(x => <action>);
Hacky way to use LINQ if you persist to use:
var result = collection.Select(c =>
{
c.PropertyToSet = c.Condition == condition ? value : otherValue;
return c;
});
But my recommendation, don't do this, you code actually get the best approach, for more readability, you can change:
foreach (var c in collection)
c.PropertyToSet = c.Condition == condition ? value : otherValue;
You can use a ternary operator in conjunction with a linq statement:
collection.ToList().ForEach(c => c.PropertyToSet = c.Condition == condition ? value : otherValue);
However I woud just use a regular foreach here to avoid converting the collection to a list.
Well, you could do:
var itemToSetValue = collection.FirstOrDefault(c => c.Condition == condition);
if(itemToSetValue != null)
itemToSetValue.PropertyToSet = value;
// Depending on what you mean, this predicate
// might be c => c != itemToSetValue instead.
foreach (var otherItem in collection.Where(c => c.Condition != condition))
{
otherItem.PropertyToSet = otherValue;
}
Now of course that's not a pure LINQ solution, but pure LINQ solutions are not appropriate for modifying existing collections.
I'm writing a query that uses FirstOrDefault after an OrderBy query, which should check if it isn't null first then use some data in it. Is there a better way than writing it like this:
int count = db.Items.Count(i =>
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault() != null
&&
i.Assignments.OrderByDescending(a =>
a.DateAssigned).FirstOrDefault().DateReturned == null)
What this code does is there are items that has many assignments, I take the latest assignment by date, then check if it exist, then run a condition on a property (DateReturned). As you see, this query is long, and most of my queries seem to look like this where I check for null first then run a second query on it using their properties. Is there a better way of doing this?
Just call .Any(a => a.DateReturned == null) to check whether there are any items that meet the condition.
If you only want to check the latest assignment, add .Take(1) before the .Any().
My take:
int count =
itemsQuery.Select(i => i.Assignments.OrderByDescending(a => a.DateAssigned))
.Count(i => i.FirstOrDefault() != null &&
i.First().DateReturned == null);
You can put the result in a variable to avoid doing the same thing twice:
int count = itemsQuery.Count(i => {
var f = i.Assignments.OrderByDescending(a => a.DateAssigned).FirstOrDefault();
return f != null && f.DateReturned == null;
});