简体   繁体   中英

“LINQ expression could not be translated” with DateTime comparison in LINQ query?

I'm a newbie that just recently started using EF Core 5 for a project and am encountering an issue with the following query:

TimeSpan bookTimeToLive = TimeSpan.FromHours(10);
IList<Book>? expiredBooks = dbContext.Value.Books.AsQueryable()
                    .Where(x => DateTime.UtcNow - x.UtcTimeStamp > bookTimeToLive)
                    .ToList();

// Get list of expired Books to remove them
dbContext.Value.RemoveRange(expiredBooks);
await dbContext.Value.SaveChangesAsync(cancellationToken);

My goal is to remove all Books that are expired (their timestamp is past the amount of time I want to keep track of them for).

With that, I get the exception:

The LINQ expression 'DbSet<Books>()
    .Where(d => DateTime.UtcNow - d.UtcTimeStamp > __bookTimeToLive_0)' could not be translated. Either
 rewrite the query in a form that can be translated, or switch to client evaluation explicitly by 
inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See 
https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Upon some digging, I realized that this is because EF is unable to parse my DateTime comparison into the SQL query, so I attempted to use the DbFunctions.DateDiffHour() method from https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.sqlserverdbfunctionsextensions.datediffhour?view=efcore-5.0#Microsoft_EntityFrameworkCore_SqlServerDbFunctionsExtensions_DateDiffHour_Microsoft_EntityFrameworkCore_DbFunctions_System_DateTime_System_DateTime _

The issue now is that even though I have the Nuget EF Core 5 installed and imported in my class, I can not access any of the DbFunctions methods:

在此处输入图像描述

The same applies to EF.Functions (no public methods): 在此处输入图像描述

Is this perhaps a bug or am I not using these classes properly? Is this the right approach for what I want to accomplish? Cheers!

If the time component is static (same for all rows) then a simple option is to apply it to the current date to form a cutoff to compare against:

Instead of:

TimeSpan bookTimeToLive = TimeSpan.FromHours(10);
IList<Book> expiredBooks = dbContext.Value.Books
                .Where(x => DateTime.UtcNow - x.UtcTimeStamp > bookTimeToLive)
                .ToList();

Something like this. No need for DbFunctions.

DateTime expiryCutoff = DateTime.UtcNow.AddHours(-10);
Ilist<Book> expiredBooks = dbContext.Books
    .Where(x => x.UtTimeStamp < expiryCutoff)
    .ToList();

If it is dynamic, DateTime methods like AddHours will still translate:

Ilist<Book> expiredBooks = dbContext.Books
    .Where(x => x.UtTimeStamp.AddHours(x.ExpiryCutoff) < DateTime.UtcNow)
    .ToList();

Where ExpiryCutoff is a data-driven value in the record. (or a related expression)

Alternatively, can we just use the DateTime object and rewrite your original query?

IList<Book>? expiredBooks = dbContext.Value.Books
                    .Where(x => x.UtcTimeStamp.addHours(10) > DateTime.UtcNow)
                    .ToList();

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