使用LinqKit时EF.Functions.DateDiffDay在Sum()中无法翻译的问题
LinqKit下EF.Functions.DateDiffDay+Sum翻译失败的解决办法
问题重现
使用LinqKit时,以下包含Sum()和EF.Functions.DateDiffDay的表达式会触发翻译错误:
public static Expression<Func<Unit, int>> CalculateTimer() { return x => x.Statuses.Sum(s => EF.Functions.DateDiffDay(x.CreatedAt, DateTime.UtcNow)); }
错误提示:
Translation of method 'Microsoft.EntityFrameworkCore.SqlServerDbFunctionsExtensions.DateDiffDay' failed
但两种场景下代码可正常运行:
- 移除
Sum()后,表达式能被正确翻译执行; - 不使用LinqKit时,保留
Sum()的代码可正常被EF Core处理。
核心原因
LinqKit在处理嵌套表达式(Sum()的委托内部调用EF内置函数)时,没有正确保留EF函数的调用标识信息,导致EF Core无法识别该方法并将其翻译为对应SQL语句。
可行解决方案
1. 用LinqKit的Expand()显式展开表达式
在使用目标表达式前调用Expand()方法,确保表达式树被正确处理,让EF Core能识别内置函数的调用:
var calcExpr = CalculateTimer().Expand(); var results = dbContext.Units.Select(calcExpr).ToList();
2. 重构表达式逻辑(适配当前场景)
因为x.CreatedAt是Unit的属性,每个Status对应的该值完全相同,所以Sum()的结果等价于DateDiffDay的计算值乘以Statuses的数量,重构后可避开嵌套表达式的处理问题:
public static Expression<Func<Unit, int>> CalculateTimer() { return x => EF.Functions.DateDiffDay(x.CreatedAt, DateTime.UtcNow) * x.Statuses.Count(); }
3. 手动构建表达式树
如果上述方案不适用,可手动拼接表达式树,确保EF函数的调用被EF Core正确识别:
public static Expression<Func<Unit, int>> CalculateTimer() { var unitParam = Expression.Parameter(typeof(Unit), "x"); // 构建DateDiffDay调用表达式 var createdAtProp = Expression.Property(unitParam, nameof(Unit.CreatedAt)); var utcNowConst = Expression.Constant(DateTime.UtcNow, typeof(DateTime)); var dateDiffMethod = typeof(SqlServerDbFunctionsExtensions) .GetMethod(nameof(SqlServerDbFunctionsExtensions.DateDiffDay), new[] { typeof(DbFunctions), typeof(DateTime), typeof(DateTime) }); var dateDiffExpr = Expression.Call( dateDiffMethod, Expression.Constant(EF.Functions), createdAtProp, utcNowConst); // 构建Sum调用表达式 var statusesProp = Expression.Property(unitParam, nameof(Unit.Statuses)); var sumMethod = typeof(Enumerable) .GetMethods() .First(m => m.Name == nameof(Enumerable.Sum) && m.GetParameters().Length == 2) .MakeGenericMethod(typeof(Status), typeof(int)); var sumExpr = Expression.Call( sumMethod, statusesProp, Expression.Lambda<Func<Status, int>>(dateDiffExpr, Expression.Parameter(typeof(Status), "s"))); return Expression.Lambda<Func<Unit, int>>(sumExpr, unitParam); }
内容的提问来源于stack exchange,提问作者Nikita
相关产品推荐
相关产品推荐

