Truncate Time Converting list to nullable - c#

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();

Related

Why are these two LINQ queries different? When they seem to be the same

I would like know why these two LINQ queries are different, or why one works and the other does not? Obviously the compiler says they are different, but I can't seem to find out why?
I want the first query to work, but no matter what I do It won't work.
The below LINQ query gives me an error of,
Cannot implicitly convert type 'int?' to
'System.Collections.Generic.IEnumerable'
public static IEnumerable<int?> GetTenantFacilityCosts(int? code, DateTime date, int number)
{
return DataContext.Facility_Cost_TBLs.
Where(p => p.Tenant_Code == code &&
p.Code_Date == number &&
p.Date_Year == date.Year &&
p.Date_Month == date.Month).
FirstOrDefault().Tenant_Portion;
}
But this query below gets the int value that I am trying to get and does not throw an error.
int? facilityCost =
DataContext.Facility_Cost_TBLs.
Where(p => p.Tenant_Code == selectedTenant.Tenant_Code &&
p.Code_Date == 10 &&
p.Date_Year == date.Year &&
p.Date_Month == date.Month).
FirstOrDefault().Tenant_Portion;
Why are these two queries differnt and how would I get the first one to work correctly?
As you can see from the one that works, the result of your query is an int?. When you've turned that to a function the return type of your function is IEnumerable<int?>.
You function should be
public static int? GetTenantFacilityCosts(int? code, DateTime date, int number)
{
...
}
The Linq query returns a nullable int i.e. int?, the reason it works for the second is because it is assigning the value to a int?.
The second one is returning a int? on a method that is trying to return: IEnumerable<int?>. If you want to return an enumerable of the results you need to remove the FirstOrDefault from the query. And judging by the method name, i.e. the plural Costs, this is what you want.
However if you only want one result change the method signature to:
public static int? GetTenantFacilityCosts(int? code, DateTime date, int number)

MVC linq to sql sum

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;

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.

LINQ to Entities does not recognize the method 'System.String ToString()' method when converting Nullable DateTime

I'm trying to get the Day of the week from a Nullable DateTime and then Joining on a list of days.
My solution was to convert it to a DateTime first, but Linq to Entities doesn't like it.
LIST is an IEnumerable<string>
suggestions?
var result = from X in model.X
where X.StartDate.HasValue
join item in LIST on
Convert.ToDateTime(X.StartDate).DayOfWeek.ToString() equals item
select X;
Converting to a methods chain is no help:
var result = model.X.Where(x => x.StartDate.HasValue).Join(LIST,x => Convert.ToDateTime(x.StartDate).DayOfWeek.ToString(), item => item, (x, item) => x);
var result = from X in model.X
where X.StartDate.HasValue &&
List.Contains(SqlFunctions.DatePart("weekday", X.StartDate))
select X;
weekday returns an int so you should have a list of integers not strings
this will work if you use .ToList() before the convertion take place.
Entity 4.0 Casting Value As DateTime
var result = model.X.Where(x => x.StartDate.HasValue).ToList().Join(LIST,x => Convert.ToDateTime(x.StartDate).DayOfWeek.ToString(), item => item, (x, item) => x);
.ToString() is not supported by Linq-To-Entities, as are a number of other extension methods.
The short (and probably inaccurate to some degree) reason is that the .ToString() in the context above is sent to Sql Server to run in a query. Sql Server has no idea what .ToString() is, and so it fails when Sql Server's query parser tries to execute it.
also, FYI
http://msdn.microsoft.com/en-us/library/bb738550.aspx
EDIT: Do not use this code. It has been left here to show you what will not work and so you can see Chris' reasons why and how to solve.
Untested:
var result = from X in model.X
where ((X.StartDate != null) && X.StartDate.HasValue)
join item in LIST on
Convert.ToDateTime(X.StartDate).DayOfWeek.ToString() equals item
select X;
If you have declared StartDate as a Nullable field, it follows you should check for null values.

LINQ to SQL lambda where [nullable object must have a value error]

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.

Categories

Resources