I'm trying to get the amount of CheckIns in the last hour from a scoped Background Service with injected DbContext.
Following queries work as expected.
var checkins = _dbContext.CheckIns.ToList();
var checkinList = checkins.Where(x => x.LogInTimeStamp.ToUniversalTime() > DateTime.UtcNow.AddHours(-1));
var checkinCount = checkins.Count(x => x.LogInTimeStamp.ToUniversalTime() > DateTime.UtcNow.AddHours(-1));
As I would like to keep it clean I've tried to write it in one line:
var oneLineCountTest = _dbContext.CheckIns.Count(x => x.LogInTimeStamp.ToUniversalTime() > DateTime.UtcNow.AddHours(-1));
var oneLineCountTest2 = (int?)_dbContext.CheckIns.Count(x => x.LogInTimeStamp.ToUniversalTime() > DateTime.UtcNow.AddHours(-1)) ?? 0;
Both of these lines return an InvalidOperationException from EFCore and I don't understand why. Anyone has an explanation or solution?
Exception thrown: 'System.Net.Sockets.SocketException' in System.Net.Sockets.dll
Exception thrown: 'System.OperationCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.OperationCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.OperationCanceledException' in System.Net.Http.dll
Exception thrown: 'System.OperationCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Net.Http.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Net.Http.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Net.Http.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.Threading.Tasks.TaskCanceledException' in System.Private.CoreLib.dll
Exception thrown: 'System.InvalidOperationException' in Microsoft.EntityFrameworkCore.dll
Thankyou
CodePudding user response:
Beware of the difference between local queries (Linq to Objects) and interpreted queries (Linq to Entities).
Your first code snippet (the one that works) uses an interpreted query to build a list. You ask the database engine for a collection of elements and they are returned and materialised into a list (the ToList() call does the materialisation). Then a local query works on that list, which is possible because the list is a local object (in memory).
The second code snippet is different. You are doing only an interpreted query (using Linq to Entities).
It means that you are asking the database engine to perform all the query: counting the number of elements satisfying a certain rule.
And it will work only if Linq to Entities is able to translate your query in SQL to be performed by the database engine.
Which likely fails here.
The culprit could be the use of the AddDays() method (see first link at the end).
Remember that:
- Linq to Object works on IEnumerable, using delegates.
- Linq to Entities works on IQueryable, using expressions trees.
So in your case, the code used in the interpreted query might be impossible to translate to SQL.
To check this, try to simplify (even if it has no functional meaning) the code inside the lambda you use as a parameter to Count, to see if you have no more exceptions.
see:
- Add Day to DateTime in EF Core 3 when Select executed
- Linq-to-EF DateTime.ToLocalTime not supported
- Datetime.UtcNow in Entity Framework query evaluated different than DateTime.UtcNow in C# IL
CodePudding user response:
if you try this query , it will not issue any exceptions
var dateTimeNow= DateTime.AddHours(-1);
Var oneLineCountTest = _dbContext.CheckIns.Count(x => x.LogInTimeStamp > DateTimeNow);
it means that EF translatore can't translate ToUniversalTime() to SQL script and this is wht you have the exception. After using ToList() all data is moved to the web server and you can use any code that is legal for c#. I think that since you are using the difference, not the time , you can count the difference without converting to UTC time, if only your application is not used all around the world.