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;
});
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 getting the error
[nullable object must have a value]
And it's point at the following part of the code
.Min(x => x.Time)
Here's the whole function.
public DateTime GetFirstRecord(long userId)
{
DateTime firstRecordDate;
using (DeviceExerciseDataDataContext context = new DeviceExerciseDataDataContext())
{
firstRecordDate = (DateTime)context.Datas.Where(x => x.UserID.Equals(userId)).Min(x => x.Time);
}
return firstRecordDate;
}
Also, how can I debug this functions lambda? When I hover over the x's, It shows no value. When when I try to print in the immediate window, it says "the name 'x' does not exist in the current context"
Edit:
So I've updated my code to this:
public DateTime? GetFirstRecord(long userId)
{
DateTime? firstRecordDate;
firstRecordDate = DateTime.Now;
using (DeviceExerciseDataDataContext context = new DeviceExerciseDataDataContext())
{
var test = context.Datas.Where(x => x.UserID.Equals(userId));
if (test != null)
firstRecordDate = (DateTime)test.Min(x => x.Time);
else
firstRecordDate = null;
}
return firstRecordDate;
}
But I'm still getting the save error.
In the DB, There are no null value's anywhere. Oh, and it doesn't go to the else, in the if/else block.
There is a good chance that context.Datas could be null or any of the Data's userId could be null or x.Time is null and you are trying to cast it as (DateTime) instead of (DateTime?).
You will have to add conditions to capture these null values in your assignment statement something like:
DateTime? datevalue = context.Datas == null
? null
: (DateTime?)context.Datas.Where(x => x.UserId != null && x.UserID.Equals(userId))
.Min(x => x.Time);
If it is possible for firstRecordDate to be null, the .Min isn't allowed.
Can you try this?
firstRecordDate = (DateTime)context.Datas.Where(x => x.UserID.Equals(userId));
if (firstRecordDate.Count() > 0) firstRecordDate = firstRecordDate.Min(x => x.Time);
if that works, then that's your problem. There are probably more elegant ways to deal with this but at least you will have a workaround to start with.
If you can be assured that at least one row has a valid time you could filter out the entries that have null as Time (or handle the case that DateTime.MaxValue is returned)
firstRecordDate = context.Datas.Where(x => x.UserID == userId)
.Select( x=> x.Time ?? DateTime.MaxValue)
.Min();
Try
(DateTime)context.Datas.Where(x => x.UserId != null && x.UserID.Equals(userId)).Min(x => x.Time)
The userID != null should evaluate first which will prevent nulls when you get to the Min. I haven't tested that but I think I remember doing stuff like it in the past.
The Min function is attempting to return the minimum value, and I've encountered this message before when no elements were being returned from the IQueryable - you need to check for the existance of elements before invoking, or declare firstRecordDate as a DateTime?.
Debugging lambda's is not supported in the watch window. If you want to debug it, I would suggest instead making it a long form lambda over multi line in order to debug
.Where(x =>
{
return x.ToString();
});
You would be able to debug the inner method code then.
You will get that error if context.Datas.Where(x => x.UserID.Equals(userId)) does not return any values.
If you use DateTime? instead of DateTime it will avoid this error by returning null.
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
}
How can I specify conditions in Where predicates in LINQ without getting null reference exceptions. For instance, if q is an IQueryable how can I do like:
Expression<Func<ProductEntity,bool>> predicate = p => !search.CategoryId.HasValue || (search.CategoryId.HasValue && search.CategoryId == p.CategoryId);
var q2 = q.Where(predicate);
Here search is an object that holds possible search conditions that may or may not be set like search.CategoryId might not be set but if it is I want to get the products that are set by that condition.
When I do this I get null reference exceptions.
You can use the null-coalescing operator ?? to replace a possible null value with a default value. The following sets tries to match the search.Category if it exists or simply creates an "always true" expression. This will be optimized by any good Linq query provider (e.g. LinqToSql).
Expression<Func<ProductEntity,bool>> predicate = p => (search.CategoryId ?? p.CategoryId) == p.CategoryId);
var q2 = q.Where(predicate);
Another possibility would be to dynamically compose a query predicate using PredicateBuilder. That's the way I do it for searches with a similar pattern as you use:
var predicate = PredicateBuilder.True<Order>();
if (search.OrderId))
{
predicate = predicate.And(a => SqlMethods.Like(a.OrderID, search.OderID);
}
// ...
var results = q.Where(predicate);
Let's dissect the line:
Expression<Func<ProductEntity,bool> predicate = p => !search.CategoryId.HasValue
|| (search.CategoryId.HasValue && search.CategoryId == p.CategoryId)
var q2 = q.Where(predicate);
So how many ways can we get null problems?
search (your "captured" variable) could be null
p could be null, meaning there is a null in the list
you've handled the case of search.CategoryId being null (Nullable<T>)
but maybe p.CategoryId (the category on a record in the list) is null (Nullable<T>) - however, I'm not sure that this would cause a NullReferenceException
q (the list / source) could be null
So: out of 5 options you've eliminated 1; look at the other 4? There is also the definite possibility that the issue is caused by something invisible not shown in the code; for example the get could be:
public int? CategoryId {
get {return innerObject.CategoryId;}
}
and innerObject could be null; if you eliminate the other 4 (pretty easy to do), look at at this one as a last resort.