【发布时间】:2020-05-11 22:28:44
【问题描述】:
我有以下代码用于将基于 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);
(Un)幸运的是,当我升级到 Entity Framework Core 3.0 时,代码抛出了 InvalidOperationException,因为表达式无法转换为 SQL 查询(尽管它只过滤了一个属性匹配数据库列):
System.InvalidOperationException: 'LINQ 表达式 '在哪里( 来源:数据库集, 谓词:(f) => Invoke(__filtering_0, f[DataLog]) )' 无法翻译。要么重写查询形式, 翻译,或通过插入显式切换到客户端评估 调用 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync()。见https://go.microsoft.com/fwlink/?linkid=2101038 更多信息。
那么你能告诉我,我应该如何修改代码以确保所有(大部分)处理都留在服务器端?保持通用代码符合标准的最佳做法是什么?
【问题讨论】:
-
你的过滤表达式可以是任何东西,因为你已经指定了一个委托,所以这取决于你真的扔给它 - 你有传递给
GetDataAsync方法的委托的例子吗?正如您所说,如果它只是一个简单的属性过滤器,那么我看不出为什么 3.0 无法翻译它。我想说这在 2.2 中起作用的唯一原因是,当无法翻译查询时,2.2 会自动切换到客户端评估。您是否检查过 2.2 确实在服务器上执行? 3 现在抛出异常以防止意外后果/性能。 -
@Charleh 我修改了最初的帖子以展示我如何使用它。我检查了一下,在 2.2 中它有很多这样的警告:“[WRN] LINQ 表达式'Skip(__p_2)' 无法翻译,将在本地进行评估。” 这是一个 SQLite数据库,但我认为,它支持这类查询。
-
使用
Expression<Func<TType, bool>> filtering代替Func<TType, bool> filtering。一般遵循Queryable方法模式,即使用Expression<Func<…>>而不是Func<…>——前者可以翻译,后者不能。 -
@IvanStoev 将参数转换为
Expression在这种特殊情况下有所帮助,因此我尝试相应地修改数据处理程序方法。如果您宣传您的评论以回答,我会接受它。 -
发现了同样的问题,我打算建议 - 如果你使用
Expression<Func<>>而不是Func<>它可以工作。经验。您通过将Func包装在另一个表达式中来构建的树实际上创建了一个原始委托链 - 即 LINQ 无法破译它,因为它不知道如何翻译调用。您可以在原始 lambda 中看到:t => Invoke(value(ConsoleApp1.Program+<>c__DisplayClass2_01[ConsoleApp1.GuidTable]).filtering, t)与将表达式作为参数时得到的结果t => (t.Guid == value(ConsoleApp1.Program+<>c__DisplayClass1_0).g)
标签: c# entity-framework-core-3.0