I have a method which takes nullable DateTime type effectiveDate as a parameter.
List<Scales> GetScales(scales scaleId, DateTime? effectiveDate, int count)
{
return scales.Where(rs => rs.ScaleID != scaleId)
.Where(rs => rs.Review.ReleaseDate >= effectiveDate)
.OrderByDescending(rs => rs.Review.ReleaseDate)
.Take(count)
.ToList();
}
This method returns a list. What I am trying to do is, if effectiveDate is null then it will not check .Where(rs => rs.Review.ReleaseDate >= effectiveDate) condition.
I can easily achieve this by a IF..ELSE condition. But I wanted to know, is it possible in a single LINQ statement ?
If you want to include all entries without effective date:
.Where(rs => !effectiveDate.HasValue || rs.Review.ReleaseDate >= effectiveDate)
You can use the conditional operator to do if-else checks.
But in your case, just use the fact that conditions that are chained with && or || will be evaluated from left to right, and evaluation of the condition is stopped after a truth value can be assigned. For the OR operator || this means that the condition is met either if the first criteria is true or the first is false and the second is true:
.Where(rs => !effectiveDate.HasValue || rs.Review.ReleaseDate >= effectiveDate)
What if you make it a compound condition saying
.Where(rs => effectiveDate.HasValue && rs.Review.ReleaseDate >= effectiveDate)
I just check this code and its works for me.
.Where(rs =>effectiveDate.HasValue? rs.Review.ReleaseDat>=effectiveDate.Value:true)
Related
I have this simple query, the problem is that the End property is DateTime? type and I would like to include the records where this value is null as well but I cannot seem to do it. I tried using the Terniary operator and still no results
await _context.Registos
.Where(r => r.Start.Date >= DateStart.Date && r.End.HasValue ? r.End.Value <= DateEnd.Date : !r.End.HasValue )
.AsNoTracking()
.ToListAsync();
A cleaner way, something like this:
await _context.Registos
.Where(r => r.Start.Date >= DateStart.Date
&& (r.End == null || r.End.Value <= DateEnd.Date))
.AsNoTracking()
.ToListAsync();
await _context.Registos
.Where(r => r.Start.Date >= DateStart.Date &&
(!r.End.HasValue || (r.End.Value <= DateEnd.Date)) )
.AsNoTracking()
.ToListAsync();
The only way of having a variable which hasn't been assigned a value in C# is for it to be a local variable - in which case at compile-time you can tell that it isn't definitely assigned by trying to read from it :)
I suspect you really want Nullable<DateTime> (or DateTime? with the C# syntactic sugar) - make it null to start with and then assign a normal DateTime value (which will be converted appropriately). Then you can just compare with null (or use the HasValue property) to see whether a "real" value has been set.
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 try to use LINQ to filter the data, the data is from 3rd party API (JIRA Server), and ResolutionDateis DateTime type, I guess it use Nullable, anyway, I am pretty sure the value is null, but when I use LINQ, it just not work at all. The LINQ just can't do i.ResolutionDate == null, it always said there is no item match this condition. I'm pretty sure I have the issues their ResolutionDate is null.
https://developer.atlassian.com/server/jira/platform/database-issue-fields/
var foo = datas.Where(i =>
i.Created > date && i.Created <= date.AddDays(7) &&
i.ResolutionDate> date.AddDays(7) && i.ResolutionDate== null);
You could probably coalesce those into (i.ResolutionDate?.Date > date.AddDays(7))
Ultimately, it should have some condition that will return true when you do the comparison.
You should check if the value of ResolutionDate is null OR more than seven days in the future
var foo = datas.Where(i =>
i.Created > date && i.Created <= date.AddDays(7) &&
(i.ResolutionDate == null || i.ResolutionDate > date.AddDays(7)));
I'm trying to sort some data that can be in one of the following (inferred) states (in this order):
live (valid StartDate, null EndDate);
draft (null StartDate);
ended (valid EndDate).
I've inherited the following syntax on a IQueryable:
iQueryableData
.OrderBy(t => t.StartDate == null ? 1 : (t.EndDate == null ? 0 : 2))
.ThenByDescending(t => t.StartDate)
.ThenBy(t => t.PartnerId)
And that is fine, since it sorts on one of the first 3 columns of the table, depending on some IF statements.
Now I need to rewrite that to work in memory (so just LINQ, no IQueryable), on a different (but similar) model. Here's what the above query will roughly translate to:
data
.OrderBy(t => t.StartDate == null
? t.EndDate // DateTime
: (t.EndDate == null
? t.Id // integer
: t.Name // string
)
)
This obviously fails to compile, because
CS0173 C# Type of conditional expression cannot be determined because
there is no implicit conversion between 'int' and 'string'
Presumably, I could keep sorting by an integer, but I've no idea what the numbers would refer to in this case (the order of the property as written inside the class? The sorted by name order of the same thing?).
Unfortunately, all questions I've found related to mine are ordering based on an IF statement that's relying on an external value (not inside the data).
Use the ThenBy extension. This ensures that the previous order is maintained while applying a new order criteria. For each specific case return the desired property to participate in order (Name, Id, EndDate) so each group in the set will be sorted by these values. Use some constant value for the other items that do not meet the original criteria so their order remains unchanged by the current ThenBy.
items
//sort by live, draft and ended
.OrderBy(t => t.StartDate == null ? 1 : (t.EndDate == null ? 0 : 2))
//keep the live, draft and ended sort,
//and for the live items apply only a sort by ID,
//but let the other items in the previous order (live, draft and ended)
//by returning the same value for each (zero or null)
.ThenBy( t=> t.StartDate != null && t.EndDate == null ? t.ID : 0)
//the same
//keep the sort by live, draft, ended and now by ID for live ones only
//but for the draft items sort them by EndDate
//leave the others unaffected by this sort
.ThenBy( t=> t.StartDate == null && t.EndDate != null ? t.EndDate : default(DateTime?))
//same - sort ended items by name
.ThenBy( t=> t.StartDate != null && t.EndDate != null ? t.Name : null)
I suggest you to implement a comparer for your data. The compareTo method will handle some complex cases you may have : what if linq have to compare Date, Number or String ?
Trying to get the values returned from a database based on the sum of a field.
But getting this message:
The cast to value type 'System.Decimal' failed because the
materialized value is null. Either the result type's generic parameter
or the query must use a nullable type.
It is valid for the database to contain no records for that user for that day, hence I went down the nullable route. In the good old days I would have built a Stored Procedure with `ISNULL` in it!!!
This is the basic expression I have:
decimal? foodCount = dbContext.fad_userFoods.Where(uf => uf.dateAdded == thisDate && uf.userID == thisGuid).Sum(uf=>(decimal?)uf.quantityAmount ?? 0m);
Googling it came up with the nullable definitions and use of the ?? with the "m" as it's decimal. But still the error persists!
Your collective help will be invaluable as ever. Thanks in advance.
Use the DefaultIfEmpty method. This will fill in a 0 if no value at all can be found.
decimal foodCount = dbContext.fad_userFoods
.Where(uf => uf.dateAdded == thisDate && uf.userID == thisGuid)
.Select(uf => uf.quantityAmount)
.DefaultIfEmpty()
.Sum();
Since it's a sum and not average you don't really mind null-values?
Why not simply removing the null-values?
decimal? foodCount = dbContext.fad_userFoods
.Where(uf =>
uf.dateAdded == thisDate &&
uf.userID == thisGuid &&
uf.quantityAmount != null)
.Sum(uf=> uf.quantityAmount);
Use Convert.ToDecimal(), this will handle your null issue.
decimal foodCount = dbContext.fad_userFoods.Where(uf => uf.dateAdded == thisDate
&& uf.userID == thisGuid)
.Sum(uf=> Convert.ToDecimal(uf.quantityAmount ?? 0m));
LINQ to Entities does not recognize the method 'System.Decimal
ToDecimal(System.Decimal)' method, and this method cannot be
translated into a store expression.
Edit:
decimal foodCount = dbContext.fad_userFoods.Where(uf => uf.dateAdded == thisDate
&& uf.userID == thisGuid)
.Sum(uf=> { decimal result;
decimal.TryParse(uf.quantityAmount,out result);
return result;});
The confusion originates from the fact the Sum in LINQ To Entities is processed a bit different than in LINQ To Objects. Although from declaration it looks like that calling it on a let say decimal will return 0, when the target set is empty actually the SQL SUM function returns NULL, even if the target column is not nullable.
Once you know that, there are two ways of resolving it.
Let say we have a table with decimal column and the original expression is
table.Sum(item => item.Column)
First way is to convert it using the pattern contained in the Maarten answer:
table.Select(item => item.Column).DefaultIfEmpty().Sum()
The second way is to explicitly convert the non nullable type to nullable inside the function and then apply null-coalescing operator to the result:
table.Sum(item => (decimal?)item.Column) ?? 0
Both ways work and produce one and the same result, so use the one that better suits your personal preferences.
For completeness, applying the second approach in your case would be to basically move the ?? 0 outside of the Sum call:
decimal foodCount = dbContext.fad_userFoods
.Where(uf => uf.dateAdded == thisDate && uf.userID == thisGuid)
.Sum(uf => (decimal?)uf.quantityAmount) ?? 0;