需求:动态拼接查询条件
遇到过得报错:
没有为类型“System.Nullable`1[System.Int32]”和“System.Int32”定义二进制运(可空字段和值类型不匹配)
LINQ to Entities中不支持LINQ表达式节点类型“Invoke”(使用拼接)
先放个简单的例子,感受一下
创建一个来源于那个EF表的参数,并命表格的名字 //var parameter = Expression.Parameter(typeof(TableName), "TableName"); 通过上述的参访问变量值对应的字段 //MemberExpression memberDynamic = Expression.PropertyOrField(parameter, "ZiDuanName"); 把值转为常量表达式 //ConstantExpression constantDynamic = Expression.Constant("ValueName"); 生成一段二元表达式 //var query = Expression.Equal(memberDynamic, constantDynamic); 通过上述的参访问变量值对应的字段 //MemberExpression memberIsDel = Expression.PropertyOrField(parameter, "IsDel"); 把值转为常量表达式 //ConstantExpression constantIsDel = Expression.Constant(1); 在二元表达式上并列加载一段二元表达式 //query = Expression.And(query, Expression.Equal(memberIsDel, Expression.Convert(Expression.Constant(a), memberIsDel.Type))); 生成lambda表达式 //var lambda = Expression.Lambda<Func<TableName, Boolean>>(query, parameter); 查找 //var newChannel = result.FirstOrDefault(lambda);
简单的并不能满足要求,条件多的话重复代码也比较多。 准备搞个帮助类。
刚开始是 参考https://blog.csdn.net/yl2isoft/article/details/53161156写,如果字段是可空类型,直接传值会报错。 在判断的时候加个类型转换,解决问题。
然后问题又来了,传多个参数时还是会报错。LINQ to Entities中不支持LINQ表达式节点类型“Invoke”,搜了一下,AsExpandable(),.Compile()几种方法都不管用。看到https://blog.csdn.net/leewhoee/article/details/8968023 解决问题。
最后放上拼接Linq的完整代码。如有问题,欢迎指教。
public class Filter_Model { public string column { get; set; }//过滤条件中使用的数据列 public string action { get; set; }//过滤条件中的操作:==、!=等 public string logic { get; set; }//过滤条件之间的逻辑关系:AND和OR public Object value { get; set; }//过滤条件中的操作的值 public string dataType { get; set; }//过滤条件中的操作的字段的类型(1.string) } public static class SplicingFilterLinqHelper { public static Expression<Func<T, bool>> GetFilterExpression<T>(List<Filter_Model> filterConditionList) { Expression<Func<T, bool>> condition = null; if (filterConditionList != null && filterConditionList.Count > 0) { foreach (Filter_Model filterCondition in filterConditionList) { Expression<Func<T, bool>> tempCondition = CreateLambda<T>(filterCondition); if (condition == null) { condition = tempCondition; } else { if ("&&".Equals(filterCondition.logic)) { condition = condition.And(tempCondition); } else { condition = condition.Or(tempCondition); } } } } return condition; } public static Expression<Func<T, bool>> CreateLambda<T>(Filter_Model filterCondition) { var parameter = Expression.Parameter(typeof(T), "p");//创建参数i var constant = Expression.Constant(filterCondition.value);//创建常数 MemberExpression member = Expression.PropertyOrField(parameter, filterCondition.column); if ("=".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.Equal(member, Expression.Convert(constant, member.Type)), parameter); } else if ("!=".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.NotEqual(member, Expression.Convert(constant, member.Type)), parameter); } else if (">".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(member, Expression.Convert(constant, member.Type)), parameter); } else if ("<".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.LessThan(member, Expression.Convert(constant, member.Type)), parameter); } else if (">=".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(member, Expression.Convert(constant, member.Type)), parameter); } else if ("<=".Equals(filterCondition.action)) { return Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(member, Expression.Convert(constant, member.Type)), parameter); } else if ("in".Equals(filterCondition.action) && "1".Equals(filterCondition.dataType)) //暂仅支持string { return GetExpressionWithMethod<T>("Contains", filterCondition); } else if ("out".Equals(filterCondition.action) && "1".Equals(filterCondition.dataType)) { return GetExpressionWithoutMethod<T>("Contains", filterCondition); } else { return null; } } public static Expression<Func<T, bool>> GetExpressionWithMethod<T>(string methodName, Filter_Model filterCondition) { ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p"); MethodCallExpression methodExpression = GetMethodExpression(methodName, filterCondition.column, filterCondition.value.ToString(), parameterExpression); return Expression.Lambda<Func<T, bool>>(methodExpression, parameterExpression); } public static Expression<Func<T, bool>> GetExpressionWithoutMethod<T>(string methodName, Filter_Model filterCondition) { ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "p"); MethodCallExpression methodExpression = GetMethodExpression(methodName, filterCondition.column, filterCondition.value.ToString(), parameterExpression); var notMethodExpression = Expression.Not(methodExpression); return Expression.Lambda<Func<T, bool>>(notMethodExpression, parameterExpression); } /// <summary> /// 生成类似于p=>p.values.Contains("xxx");的lambda表达式 /// parameterExpression标识p,propertyName表示values,propertyValue表示"xxx",methodName表示Contains /// 仅处理p的属性类型为string这种情况 /// </summary> /// <param name="methodName"></param> /// <param name="propertyName"></param> /// <param name="propertyValue"></param> /// <param name="parameterExpression"></param> /// <returns></returns> private static MethodCallExpression GetMethodExpression(string methodName, string propertyName, string propertyValue, ParameterExpression parameterExpression) { var propertyExpression = Expression.Property(parameterExpression, propertyName); MethodInfo method = typeof(string).GetMethod(methodName, new[] { typeof(string) }); var someValue = Expression.Constant(propertyValue, typeof(string)); return Expression.Call(propertyExpression, method, someValue); } } public static class LinqBuilder { /// <summary> /// 默认True条件 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static Expression<Func<T, bool>> True<T>() { return f => true; } /// <summary> /// 默认False条件 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static Expression<Func<T, bool>> False<T>() { return f => false; } public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.And); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.Or); } public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter map (from parameters of second to parameters of first) var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } } public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> map; public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) { this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) { return new ParameterRebinder(map).Visit(exp); } protected override Expression VisitParameter(ParameterExpression p) { ParameterExpression replacement; if (map.TryGetValue(p, out replacement)) { p = replacement; } return base.VisitParameter(p); } }