c# - 如何修改基于表达式的过滤器以避免在 Entity Framework Core 3.0 中进行客户端评估
问题描述
我有以下代码用于将Func
基于过滤器的过滤器转换为Expression
和过滤Entity Framework Core 2.2中的数据:
public async Task<TType> GetDataAsync<TType>(Func<TType, bool> filtering = null) where TType : class
{
Expression<Func<TType, bool>> filteringExpression = (type) => filtering(type);
if (filtering != null)
//return await myContext.Set<TType>().FirstOrDefaultAsync(filteringExpression);
return await myContext.Set<TType>().Where(filteringExpression ).FirstOrDefaultAsync();
return await myContext.Set<TType>().FirstOrDefaultAsync();
}
这就是我使用它的方式:
public async Task<DataLog> GetDataLogByID(Guid dataLogID) => await GetDataAsync<DataLog>(dataLog => dataLog.ID == dataLogID);
(不)幸运的是,当我升级到Entity Framework Core 3.0时,代码抛出了一个InvalidOperationException
,因为表达式无法转换为 SQL 查询(尽管它只过滤与数据库列匹配的属性):
System.InvalidOperationException: '无法翻译 LINQ 表达式'Where(source: DbSet, predicate: (f) => Invoke(__filtering_0, f[DataLog]) )'。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038。
那么你能告诉我,我应该如何修改代码以确保所有(大部分)处理都留在服务器端?保持通用代码但符合标准的最佳实践是什么?
解决方案
恭喜,您发现了 EF Core 3.0 中的一项重大更改 -不再在客户端上评估 LINQ 查询
旧行为
在 3.0 之前,当 EF Core 无法将作为查询的一部分的表达式转换为 SQL 或参数时,它会自动评估客户端上的表达式。默认情况下,客户端对可能昂贵的表达式的评估只会触发警告。
新行为
从 3.0 开始,EF Core 仅允许在客户端评估顶级投影(查询中的最后一个 Select() 调用)中的表达式。当查询的任何其他部分中的表达式无法转换为 SQL 或参数时,将引发异常。
有关更多信息,请参阅文档(上面的链接),但是您在升级之前遇到的警告现在正在生成 InvalidOperationExceptions 并且与 SQLite 无关,您会遇到与 SQL Server 相同的问题。
解决此问题的唯一方法是确保您的过滤表达式/函数可以转换为适当的 SQL... 或恢复为 EF Core < 3.0
更新
您可以尝试不包装传递的 Func 并将参数类型更改为Expression<Func<TType, bool>>
(它不需要对调用该方法的代码进行任何更改)
public async Task<TType> GetDataAsync<TType>(Expression<Func<TType, bool>> filter = null)
where TType : class
{
var query = myContext.Set<TType>();
if (filter != null)
query = query.Where(filter);
return await query.FirstOrDefaultAsync();
}
刚刚注意到调用GetDataAsync
似乎不正确,并且有一个额外的类型参数Guid
,应该从这个例子中删除。
public async Task<DataLog> GetDataLogByID(Guid dataLogID) =>
await GetDataAsync<DataLog>(dataLog => dataLog.ID == dataLogID);
推荐阅读
- r - 通过数据框中的组获取列的最大值
- java - 使用 2D 数组超出内存限制
- sql - 如果列的所有值都不同于某个字符串,则选择具有相同 id 的行
- macos - Carthage:安装 Carthage 时出错“在 `brew link` 步骤中发生意外错误”
- r - 如何在多个模型中使用 broom::tidy?
- spring-boot - 无法从 Kubernetes 中的 Spring Boot 客户端从远程 Spring 配置中获取值
- javascript - 当我希望第一个先超时时,间隔会立即刷新
- javascript - 反应道具不在父组件中呈现,甚至在同一个组件中
- postgresql - 在 Ubuntu 18.04 上安装 postgis 2.2.1
- python - 并行运行函数,但继承前一个函数的动态数据