我有以下代码,这些代码用于将基于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 时,由于表达式不能转换为SQL查询,因此代码抛出了InvalidOperationException
(尽管它只过滤了一个与数据库列匹配的属性):
System.InvalidOperationException:'LINQ表达式 '哪里( 来源:DbSet, 谓词:(f)=>调用(__filtering_0,f [DataLog]))'无法翻译。可以采用以下形式重写查询: 翻译,或通过插入 调用AsEnumerable(),AsAsyncEnumerable(),ToList()或 ToListAsync()。有关详情,请参见https://go.microsoft.com/fwlink/?linkid=2101038 更多信息。
那么您能告诉我,我应该如何修改代码以确保所有(大部分)处理都留在服务器端?保持通用代码符合标准的最佳实践是什么?
答案 0 :(得分:2)
恭喜,您已经发现EF Core 3.0中的一项重大更改-https://docs.microsoft.com/en-us/office/vba/language/concepts/getting-started/64-bit-visual-basic-for-applications-overview
旧行为
在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);