简体   繁体   中英

Linq Count List InvalidOperationException on no values

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

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:

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM