附加到表达式

我按照这个主题: 链接文本

杰森给出了一个例子:

public static Expression AndAlso(this Expression left, Expression right) { return Expression.Lambda(Expression.AndAlso(left, right), left.Parameters); } 

及其用法如下:

 Expression<Func> clientWhere = c => true; if (filterByClientFName) { clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName); } if (filterByClientLName) { clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName); } 

我有一个订单表,我按照上面的例子,更改列名,我得到了post创建者所具有的类似错误

二进制运算符AndAlso没有为类型’System.Func 2[Models.Order,System.Boolean]' and 'System.Func 2 [Models.Order,System.Boolean]’定义。

任何人都对我失踪的事有任何想法?

更新:

Eric,我进一步关注了上一篇文章的用户所要求的内容,这里是链接文本

用户有这个

 Expression<Func> clientWhere = c => true; Expression<Func> orderWhere = o => true; Expression<Func> productWhere = p => true; if (filterByClient) { clientWhere = c => c.ClientID == searchForClientID; } 

现在,如果他在filterByClient有各种条件,比如说他有clientid和/或其他一些列名,那么如何构建clientWhere表达式呢?

您正在尝试构建表示此的表达式树:

 c => true && c.ClientFName == searchForClientFName 

您实际上正在构建一个表示以下内容的表达式树:

 c => c=> true && c => c.ClientFName == searchForClientFName 

这毫无意义。

现在,你可能会天真地认为这会起作用:

 public static Expression AndAlso(this Expression left, Expression right) { // NOTICE: Combining BODIES: return Expression.Lambda(Expression.AndAlso(left.Body, right.Body), left.Parameters); } 

这会产生一些代表性的东西

 c => true && c.ClientFName == searchForClientFName 

哪个看起来正确。 但实际上这很脆弱。 假设你有

 ... d => d.City == "London" ... ... c => c.ClientName == "Fred Smith" ... 

并且您使用此方法来组合它们。 你会得到一个对象代表

 c => d.City == "London" && c.ClientName == "Fred Smith" 

到底在做什么?

此外,参数由对象标识匹配,而不是由参数名称匹配。 如果你这样做

 ... c => c.City == "London" ... ... c => c.ClientName == "Fred Smith" ... 

并将它们组合成

 c => c.City == "London" && c.ClientName == "Fred Smith" 

你在同一条船上; “c.City”中的“c”与其他两个c不同

你真正需要做的是创建一个第三个参数对象,在它们参数的每个出现时将它替换为两个lambda的实体,然后从结果替换体中构建一个新的lambda表达式树

您可以通过编写遍历表达式树体的访问者来构建替换引擎,并在其进行时重写。

我很难理解hvd的答案,所以我创建了一些代码来以不同的方式解释它。 hvd应该得到建议ExpressionVisitor的功劳。 我只是无法理解我正在使用的Linq to X类型输入函数的上下文中的示例。

我希望这有助于其他人从这个角度提出问题。

此外,我创建了组合代码作为扩展方法,使其更容易使用。


 using System; using System.Collections.Generic; using System.Linq.Expressions; namespace ConsoleApplication3 { class Program { static void Main(string[] args) { var combined = TryCombiningExpressions(c => c.FirstName == "Dog", c => c.LastName == "Boy"); Console.WriteLine("Dog Boy should be true: {0}", combined(new FullName { FirstName = "Dog", LastName = "Boy" })); Console.WriteLine("Cat Boy should be false: {0}", combined(new FullName { FirstName = "Cat", LastName = "Boy" })); Console.ReadLine(); } public class FullName { public string FirstName { get; set; } public string LastName { get; set; } } public static Func TryCombiningExpressions(Expression> func1, Expression> func2) { return func1.CombineWithAndAlso(func2).Compile(); } } public static class CombineExpressions { public static Expression> CombineWithAndAlso(this Expression> func1, Expression> func2) { return Expression.Lambda>( Expression.AndAlso( func1.Body, new ExpressionParameterReplacer(func2.Parameters, func1.Parameters).Visit(func2.Body)), func1.Parameters); } public static Expression> CombineWithOrElse(this Expression> func1, Expression> func2) { return Expression.Lambda>( Expression.AndAlso( func1.Body, new ExpressionParameterReplacer(func2.Parameters, func1.Parameters).Visit(func2.Body)), func1.Parameters); } private class ExpressionParameterReplacer : ExpressionVisitor { public ExpressionParameterReplacer(IList fromParameters, IList toParameters) { ParameterReplacements = new Dictionary(); for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++) ParameterReplacements.Add(fromParameters[i], toParameters[i]); } private IDictionary ParameterReplacements { get; set; } protected override Expression VisitParameter(ParameterExpression node) { ParameterExpression replacement; if (ParameterReplacements.TryGetValue(node, out replacement)) node = replacement; return base.VisitParameter(node); } } } } 

如果你需要它,我创建了一个小的流畅的库来动态创建lambda函数,而无需直接处理System.Linq.Expressions。 它可以轻松应对这种情况。 举个例子:

 static void Main(string[] args) { var firstNameCompare = ExpressionUtil.GetComparer((a) => a.FirstName); var lastNameCompare = ExpressionUtil.GetComparer((a) => a.LastName); Func combined = (a) => firstNameCompare(a, "Dog") && lastNameCompare(a, "Boy"); var toCheck = new FullName {FirstName = "Dog", LastName = "Boy"}; Console.WriteLine("Dog Boy should be true: {0}", combined(toCheck)); toCheck = new FullName {FirstName = "Cat", LastName = "Boy"}; Console.WriteLine("Cat Boy should be false: {0}", combined(toCheck)); Console.ReadLine(); } 

GetComparer方法寻找作为表达式传递的属性并找到ho来获取其值,然后它构建一个新的Expression来处理comparaison。

最后,调用两个函数调用“组合”函数。

如果您需要更多validation,可以使用数组并在“组合lambda”内迭代它

该库的代码和文档在这里: Kendar Expression Builder虽然nuget包在这里: Nuget Expression Builder

我试图实现这种东西。 花了一天时间才发现。 我的解决方案基于基于谓词数组的循环中的filter。 作为一个注释,它完全是Generic和基于Reflection,因为关于类和字段的唯一信息是String。 为简单起见,我直接调用Model类,但在项目中,您应该通过调用Model的控制器来进行调用。

所以这里我们去:Model部分,其中T是类中的Generic

  public class DALXmlRepository where T : class { public T GetItem(Array predicate) { IQueryable QueryList = null; QueryList = ObjectList.AsQueryable().Where((Expression>)predicate.GetValue(0)); for (int i = 1; i < predicate.GetLength(0); i++) { QueryList = QueryList.Where((Expression>)predicate.GetValue(i)); } if (QueryList.FirstOrDefault() == null) throw new InvalidOperationException(this.GetType().GetGenericArguments().First().Name + " not found."); return QueryList.FirstOrDefault(); } } 

现在LambdaExpression构建器,它是一个基础的(使用String类型或其他东西),你可以用更多的function来改进它:

  private static Expression BuildLambdaExpression(Type GenericArgument, string FieldName, string FieldValue) { LambdaExpression lambda = null; Expression Criteria = null; Random r = new Random(); ParameterExpression predParam = Expression.Parameter(GenericArgument, r.Next().ToString()); if (GenericArgument.GetProperty(FieldName).PropertyType == typeof(string)) { Expression left = Expression.PropertyOrField(predParam, FieldName); Expression LefttoUpper = Expression.Call(left, "ToUpper", null, null); //Type du champ recherché Type propType = GenericArgument.GetProperty(FieldName).PropertyType; Expression right = Expression.Constant(FieldValue, propType); Expression RighttoUpper = Expression.Call(right, "ToUpper", null, null); Criteria = Expression.Equal(LefttoUpper, RighttoUpper); } else { Expression left = Expression.PropertyOrField(predParam, FieldName); Type propType = GenericArgument.GetProperty(FieldName).PropertyType; Expression right = Expression.Constant(Convert.ChangeType(FieldValue, propType), propType); Criteria = Expression.Equal(left, right); } lambda = Expression.Lambda(Criteria, predParam); return lambda; } 

现在调用函数:

  public static Hashtable GetItemWithFilter(string Entity, XMLContext contextXML, Hashtable FieldsNameToGet, Hashtable FieldFilter) { //Get the type Type type = Type.GetType("JP.Model.BO." + Entity + ", JPModel"); Type CtrlCommonType = typeof(CtrlCommon<>).MakeGenericType( type ); //Making an instance DALXmlRepository XMLInstance = new DALXmlRepository(contextXML); ConstructorInfo ci = CtrlCommonType.GetConstructor(new Type[] { typeof(XMLContext), typeof(String) }); IControleur DalInstance = (IControleur)ci.Invoke(new object[] { contextXML, null }); //Building the string type Expression> to init the array Type FuncType = typeof(Func<,>).MakeGenericType( type ,typeof(bool)); Type ExpressType = typeof(Expression<>).MakeGenericType(FuncType); Array lambda = Array.CreateInstance(ExpressType,FieldFilter.Count); MethodInfo method = DalInstance.GetType().GetMethod("GetItem", new Type[] { lambda.GetType() }); if (method == null) throw new InvalidOperationException("GetItem(Array) doesn't exist for " + DalInstance.GetType().GetGenericArguments().First().Name); int j = 0; IDictionaryEnumerator criterias = FieldFilter.GetEnumerator(); criterias.Reset(); while (criterias.MoveNext()) { if (!String.IsNullOrEmpty(criterias.Key.ToString())) { lambda.SetValue(BuildLambdaExpression(type, criterias.Key.ToString(), criterias.Value.ToString()),j); } else { throw new JPException(JPException.MessageKey.CONTROLER_PARAMFIELD_EMPTY, "GetItemWithFilter", criterias.Key.ToString()); } j++; } Object item = method.Invoke(DalInstance, new object[] { lambda }); } 

参数是:字符串实体:实体类名称。 XMLContext:它是存储库的工作单元,我用来初始化Model类的参数Hashtable FieldsNameToGet:我想要获取的字段列表的索引/值Hashtable FieldFilter:具有FieldName / Content的键/值制作Lambda表达式

祝好运。