附加到表达式
我按照这个主题: 链接文本
杰森给出了一个例子:
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表达式
祝好运。