MVC linq to sql sum - c#

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;

Related

Entity Framework Core where clause extra condiition

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.

LINQ statement using one of two parameters

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.

Truncate Time Converting list to nullable

public List<DateTime> CalorieDates(int patientId)
{
using (var db = new DbConn())
{
List<DateTime> query =
db.Calories.Where(d => d.PatientId == patientId && d.FoodId != "initial" && d.DateOfEntry != null)
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry)).Distinct().ToList();
return query;
}
}
Why is this converting my list to nullable datetimes?
Error CS0029 Cannot implicitly convert type
'System.Collections.Generic.List<System.DateTime?>'
to 'System.Collections.Generic.List<System.DateTime>'
How do I prevent this? I do not want a list of nullable datetimes.
For some unknown reason (probably to make the query providers life easier), DbFunctions class does not provide separate TruncateTime overloads for DateTime and DateTime?, but single method with DateTime? argument which handles both cases. However, as a side effect it's changing the expression type to DateTime? even if the argument is DateTime (as it seems to be in your case).
So you need to correct that. Once you know that you are always passing DateTime (hense the result cannot be null), you can simply add .Value at the end of the TruncateTime call and the issue is solved.
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry).Value)
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry))
.Distinct().ToList().Cast<List<DateTime>>()
Like #stuartd says in comments, TruncateTime is probably returning DateTime?; your Select returns IEnumerable<DateTime?> because that's what your lambda gives it from TruncateTime, Distinct() returns the same, and ToList() converts it into List<DateTime?> because, hey that's what IEnumerable<DateTime?>.ToList() returns.
So you end up with a list of DateTime?. That's easy. If you don't want that, Select something else.
Now, I'm going to assume that TruncateTime returns a nullabe type because it may return null. My guess is that its parameter is DateTime?, and it'll only return null if you pass in a null. Since you're passing in a struct, it probably can't return null in your particular usage of it, but I don't know so I'll play it safe.
So you want to convert your enumeration of nullable datetimes to regular ones, without casting any null nullables to regular DateTime. So first filter down to the ones that aren't null, then select their values.
.Select(d => System.Data.Entity.DbFunctions.TruncateTime(d.DateOfEntry))
.Where(d => d.HasValue)
.Select(d => d.Value)
.ToList();
It looks like the db method you are calling to truncate the datetime simply removes the time portion
If that is the case then use the following instead.
List<DateTime> query = db.Calories.Where(d => d.PatientId == patientId && d.FoodId != "initial" && d.DateOfEntry != null)
.Select(d => d.DateOfEntry.Date).Distinct().ToList();

Casting a value to Decimal

Here is my code:
var finiGames = myRepos.Games
.Where(x => x.StatusGameId == (int)StatusGameEnum.Finish
&& x.EndDateTime > DateTime.Today)
.DefaultIfEmpty();
//error line
decimal? sum = finiGames.Sum(x => x.WinCost);
The error I am getting:
Error converting cast a value type "Decimal", because materializuemoe
value is null. The overall result of the type parameter or a request
to use a type that allows the value null.
What is the proper way to get a decimal??
You need to cast the WinCost to a nullable decimal inside the Sum
decimal? sum = finiGames.Sum(x => (decimal?)x.WinCost);
Try adding a ToList() to finiGames. It might kill your performance, but EF probably can't handle the conversion in the data (SQL) layer.
decimal sum = ((decimal?)finiGames.Sum(x => x.WinCost)) ?? 0;

Method using LINQ in my Repository that gets null errors

I have a Method in my LINQ
Basicly what my LINQ query does is that it retrieves data from my database.
A user can choose between two dates and retrieve the data between those two dates. But if a user choose on a date range which there is no data in I recieve this error:
The cast to value type 'Double' failed because the materialized value
is null. Either the result type's generic parameter or the query must
use a nullable type
It should be okey for the users to search between the two dates even if there is no data, it should give a error message that there is no data between those dates instead of this error I recieve in my Visual studio.
I am using MVC entity framework model first.
Here is the method:
public List<CoreValueAndAverageGrade> GetAverageGradeForAllCoreValues(
OfficeStatisticQueryViewModel model)
{
var StartDate = DateTime.Parse(model.StartDate);
var EndDate = DateTime.Parse(model.EndDate);
return db.CoreValue
.Where(v => v.CoreValueQuestion
.Any(q => !q.SubjectType.Ignored_Statistic))
.Select(coreValue => new CoreValueAndAverageGrade
{
CoreValue = coreValue,
AverageGrade = coreValue.CoreValueQuestion
.Where(q => !q.SubjectType.Ignored_Statistic)
.Average(q => q.SelectedQuestions
.Where(s =>
s.GoalCardQuestionAnswer != null
&& s.GoalCardQuestionAnswer.Grade.HasValue
&& s.GoalCard.Completed_Date >= StartDate
&& s.GoalCard.Completed_Date <= EndDate
)
.Average(s => s.GoalCardQuestionAnswer.Grade.Value))
})
.ToList();
}
Update: Grade is Double and Nullable
Any kind of help is very appreciated!
Thanks in advance!
Looking at the last part of the query - if there are no rows returned after filtering with the where clause, then s.GoalCardQuestionAnswer.Grade.Value will definitely throw because you're trying to access properties of an object that is null.
I suppose that if you change the code to this you would get 0 as the final value - then you'd need to check for this value explicitly in your code.
.Average(s => s != null ? s.GoalCardQuestionAnswer.Grade.Value : 0)
Otherwise, breaking the query down would be a good idea - it helps both debugging and readability of the code.
I would split up the queries and investigate further. My guess would be that there is some inconsistent data. Probably it happens somewhere where you compare a double with a double? and the double? value is null. Maybe check your records for nulls, which should actually be there.
That error usually comes up when you are trying to apply an aggregate operator to a result set with no records. Look at where you are using .Average and I am sure you have a query that returns no records right before.

Categories

Resources