mon*_*see 6 c# .net-core entity-framework-core-3.0
我正在尝试将解决方案升级到新的Core Framework 3.0.0。现在我有一个小问题,我不明白。
看,此方法在2.2.6中没有问题:
public async Task<IEnumerable<ApplicationUser>> GetBirthdayUsersCurrentMonth()
{
return await ApplicationDbContext.Users
.Where(x => x.Gender != ApplicationUser.GenderTypes.generic)
.Where(x => x.BirthDate.GetValueOrDefault().Month == DateTime.Now.Month)
.Where(x => x.RetireDate == null)
.OrderBy(x => x.BirthDate.GetValueOrDefault())
.ToListAsync();
}
Run Code Online (Sandbox Code Playgroud)
现在在3.0.0中,我收到一个Linq错误,它是这样说的:
InvalidOperationException:LINQ表达式'Where(source:where(source:DbSet,predicate:(a)=>(int)a.Gender!= 0),谓词:(a)=> a。 == DateTime.Now.Month)'无法翻译。以可以翻译的形式重写查询,或者通过插入对AsEnumerable(),AsAsyncEnumerable(),ToList()或ToListAsync()的调用来显式切换到客户端评估
当我禁用此行时:
.Where(x => x.BirthDate.GetValueOrDefault().Month == DateTime.Now.Month)
Run Code Online (Sandbox Code Playgroud)
错误消失了,但是我当然会吸引所有用户。而且我在此查询中看不到错误。这可能是EF Core 3.0.0中的错误吗?
原因是在EF Core 3中禁用了隐式客户端评估。
这意味着以前,您的代码未在服务器上执行WHERE子句。取而代之的是,EF将所有行加载到内存中,并在内存中评估了表达式。
要在升级后解决此问题,首先,您需要确定EF不能完全转换为SQL的内容。我的猜测是对的调用GetValueOrDefault(),因此请尝试像这样重写它:
.Where(x => x.BirthDate != null && x.BirthDate.Value.Month == DateTime.Now.Month)
Run Code Online (Sandbox Code Playgroud)
小智 6
当您尝试将解决方案的 .netCore 版本升级到 3.0 时,我将在执行升级的人员的范围内回答您的问题:
通过引用EF Core 3.0 破坏性更改官方文档,您会发现该行
不再在客户端评估 LINQ 查询
您下面的查询将不再在客户端进行评估,因为 EF 无法解释 GetValueOrDefault():
.Where(x => x.BirthDate.GetValueOrDefault().Month == DateTime.Now.Month)
Run Code Online (Sandbox Code Playgroud)
这在 3.0 之前工作的原因是因为它评估无法转换为原始 SQL 的段之前的所有内容,然后在客户端 (c#) 端评估其余的段。这意味着您的代码被粗略评估为:
return (await ApplicationDbContext.Users
.Where(x => x.Gender != ApplicationUser.GenderTypes.generic).ToListAsync()) //sql evaluated till here
.Where(x => x.BirthDate.GetValueOrDefault().Month == DateTime.Now.Month)
.Where(x => x.RetireDate == null)
.OrderBy(x => x.BirthDate.GetValueOrDefault())
.ToList();
Run Code Online (Sandbox Code Playgroud)
这在 EF Core 3.0 中不再允许,因为隐藏客户端评估在具有较大数据集的生产中是不利的,而在开发中,可能会忽略性能影响。
您有 2 个解决方案。
首选是将受影响的行重写为类似这样的内容, defaultMonthValue 是一个 const int ,其中包含一些在 GetValueOrDefault() 扩展中使用的默认月份整数。
.Where(x => (x.BirthDate != null && x.BirthDate.Value.Month == DateTime.Now.Month) || (x.BirthDate == null && defaultMonthValue == DateTime.Now.Month))
Run Code Online (Sandbox Code Playgroud)
第二个但不推荐的解决方案是在问题段之前显式添加 .AsEnumerable() 以强制 EF 评估先前的语句。
.AsEnumerable() // switches to LINQ to Objects
.Where(x => x.BirthDate.GetValueOrDefault().Month == DateTime.Now.Month)
Run Code Online (Sandbox Code Playgroud)
对于打算从 2.2 迁移到 3.0 并希望在实际迁移之前测试 2.2 代码库中的客户端评估重大更改的人的一些提示:
从Microsoft docs 开始,将以下内容添加到您的 startup.cs 以模拟 3.0 客户端查询抛出。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2280 次 |
| 最近记录: |