EF 6查询中能否在Expression体中传入函数调用(C# 7)
问题原因
你定义的ActiveAccountPredicate是表达式树(Expression<Func<vAccount, bool>>),而非普通委托(Func<vAccount, bool>)。当EF Core这类LINQ提供者解析表达式树生成SQL时,无法将自定义的IsActive方法调用转换为对应的SQL逻辑——它既不会执行这个方法,也无法解析方法体内容,最终导致该条件被忽略或查询报错。
可行实现方式
有两种常用方案解决这个问题:
方案一:将IsActive重构为可复用的表达式树
把IsActive改成返回表达式树,再通过辅助方法将其与其他条件组合,既保留逻辑复用性,又能被LINQ提供者正常解析。
步骤1:改写IsActive为表达式树
public static Expression<Func<vAccount, bool>> IsActive() { return a => (a.DepartedDate == null || a.DepartedDate >= DateTime.Now) && (a.RegistrationTypeId != RegistrationType.NotRegistered || a.SupoDepartedDate == null || a.SupoDepartedDate >= DateTime.Now) && (a.RegistrationTypeId != RegistrationType.NotRegistered || a.RegistrationExpiredDate == null || a.RegistrationExpiredDate >= DateTime.Now); }
步骤2:添加表达式组合辅助工具
用于统一不同表达式树的参数,避免参数不匹配问题:
// 合并两个"与"条件的表达式 private static Expression<Func<T, bool>> CombineAnd<T>(Expression<Func<T, bool>> leftExpr, Expression<Func<T, bool>> rightExpr) { var param = leftExpr.Parameters[0]; // 替换右表达式的参数为左表达式的参数 var rightBody = new ParameterReplacer(rightExpr.Parameters[0], param).Visit(rightExpr.Body); return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(leftExpr.Body, rightBody), param); } // 参数替换器,用于表达式树参数统一 private class ParameterReplacer : ExpressionVisitor { private readonly ParameterExpression _oldParam; private readonly ParameterExpression _newParam; public ParameterReplacer(ParameterExpression oldParam, ParameterExpression newParam) { _oldParam = oldParam; _newParam = newParam; } protected override Expression VisitParameter(ParameterExpression node) { return node == _oldParam ? _newParam : base.VisitParameter(node); } }
步骤3:重构ActiveAccountPredicate
protected Expression<Func<vAccount, bool>> ActiveAccountPredicate { get { var isActiveExpr = IsActive(); // 构建其他条件的表达式树 var otherConditionsExpr = Expression.Lambda<Func<vAccount, bool>>( Expression.AndAlso( Expression.OrElse(Expression.Not(Expression.PropertyOrField(isActiveExpr.Parameters[0], "Foreign")), Expression.Constant(IsShowForeign)), Expression.OrElse(Expression.NotEqual(Expression.PropertyOrField(isActiveExpr.Parameters[0], "RegistrationTypeId"), Expression.Constant(RegistrationType.NotRegistered)), Expression.Constant(IsShowNotRegistered)) ), isActiveExpr.Parameters[0] ); // 合并两个表达式 return CombineAnd(isActiveExpr, otherConditionsExpr); } }
方案二:直接内联IsActive逻辑
如果不需要复用IsActive的逻辑,直接把方法体内容内联到表达式树中,最简单直接:
protected Expression<Func<vAccount, bool>> ActiveAccountPredicate => a => ( (a.DepartedDate == null || a.DepartedDate >= DateTime.Now) && (a.RegistrationTypeId != RegistrationType.NotRegistered || a.SupoDepartedDate == null || a.SupoDepartedDate >= DateTime.Now) && (a.RegistrationTypeId != RegistrationType.NotRegistered || a.RegistrationExpiredDate == null || a.RegistrationExpiredDate >= DateTime.Now) ) && (!a.Foreign || IsShowForeign) && (a.RegistrationTypeId != RegistrationType.NotRegistered || IsShowNotRegistered);
补充说明
如果你的查询是针对内存集合(LINQ to Objects),而非ORM数据库查询,可以把ActiveAccountPredicate的类型改为Func<vAccount, bool>,此时IsActive方法会被正常执行,但这种方式无法生成SQL,只能在内存中过滤数据。
内容的提问来源于stack exchange,提问作者Intolighter
相关产品推荐
相关产品推荐

