c# – 动态linq建筑表达式
我需要为动态搜索创建一个动态
linq表达式.基本搜索工作正常,但无法使用集合.
我能够获得该书的标题和作者,但未能获得所需的页面标题. 我在行中获得了异常“left11 = Expression.Property(page1,”Heading“);”. 我认为我构建的表达式无法识别List.怎么可能这样呢? 请参阅以下代码和stacktrace异常. using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text; using System.Threading.Tasks; namespace XMLStorageAndFilter { public class Books { public Books() { Page = new List<Page>(); } public string Title { get; set; } public Author Author { get; set; } public List<Page> Page { get; set; } } public class Author { public string FirstName { get; set; } } public class Page { public string Heading { get; set; } } public class Program2 { static void Main() { Page page = new Page(); page.Heading = "Heading"; Books bok = new Books(); bok.Title = "Title"; bok.Author = new Author() { FirstName = "FirstName" }; bok.Page.Add(page); List<Books> testList = new List<Books>(); testList.Add(bok); IQueryable<Books> queryableTestData = testList.AsQueryable<Books>(); ParameterExpression pe11 = Expression.Parameter(typeof(Books),"p"); Expression left11 = Expression.Property(pe11,"Title"); Expression right11 = Expression.Constant("Title"); Expression e11 = Expression.Equal(left11,right11); var author = Expression.Property(pe11,"Author"); left11 = Expression.Property(author,"FirstName"); right11 = Expression.Constant("FirstName"); Expression e21 = Expression.Equal(left11,right11); Expression predicateBody11 = Expression.And(e11,e21); Expression<Func<Books,bool>> condition = Expression.Lambda <Func<Books,bool>>(predicateBody11,new ParameterExpression[] { pe11 }); var q = queryableTestData.Where(condition); var page1 = Expression.Property(pe11,"Page"); left11 = Expression.Property(page1,"Heading"); right11 = Expression.Constant("Heading"); Expression e22 = Expression.Equal(left11,right11); Expression predicateBody12 = Expression.And(e11,e22); Expression<Func<Books,bool>> condition2 = Expression.Lambda <Func<Books,bool>>(predicateBody12,new ParameterExpression[] { pe11 }); var qq1 = queryableTestData.Where(condition2); } } }
解决方法
您可以使用
here中描述的方法.
您需要将方法的结果强制转换为Expression< Func< T,bool>>.是你的类型. 我回家后会提供一个完整的例子. 编辑: using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Collections; using System.Reflection; namespace ExpressionPredicateBuilder { public enum OperatorComparer { Contains,StartsWith,EndsWith,Equals = ExpressionType.Equal,GreaterThan = ExpressionType.GreaterThan,GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual,LessThan = ExpressionType.LessThan,LessThanOrEqual = ExpressionType.LessThan,NotEqual = ExpressionType.NotEqual } public class ExpressionBuilder { public static Expression<Func<T,bool>> BuildPredicate<T>(object value,OperatorComparer comparer,params string[] properties) { var parameterExpression = Expression.Parameter(typeof(T),typeof(T).Name); return (Expression<Func<T,bool>>)BuildNavigationExpression(parameterExpression,comparer,value,properties); } private static Expression BuildNavigationExpression(Expression parameter,object value,params string[] properties) { Expression resultExpression = null; Expression childParameter,predicate; Type childType = null; if (properties.Count() > 1) { //build path parameter = Expression.Property(parameter,properties[0]); var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type); //if it′s a collection we later need to use the predicate in the methodexpressioncall if (isCollection) { childType = parameter.Type.GetGenericArguments()[0]; childParameter = Expression.Parameter(childType,childType.Name); } else { childParameter = parameter; } //skip current property and get navigation property expression recursivly var innerProperties = properties.Skip(1).ToArray(); predicate = BuildNavigationExpression(childParameter,innerProperties); if (isCollection) { //build subquery resultExpression = BuildSubQuery(parameter,childType,predicate); } else { resultExpression = predicate; } } else { //build final predicate resultExpression = BuildCondition(parameter,properties[0],value); } return resultExpression; } private static Expression BuildSubQuery(Expression parameter,Type childType,Expression predicate) { var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2); anyMethod = anyMethod.MakeGenericMethod(childType); predicate = Expression.Call(anyMethod,parameter,predicate); return MakeLambda(parameter,predicate); } private static Expression BuildCondition(Expression parameter,string property,object value) { var childProperty = parameter.Type.GetProperty(property); var left = Expression.Property(parameter,childProperty); var right = Expression.Constant(value); var predicate = BuildComparsion(left,right); return MakeLambda(parameter,predicate); } private static Expression BuildComparsion(Expression left,Expression right) { var mask = new List<OperatorComparer>{ OperatorComparer.Contains,OperatorComparer.StartsWith,OperatorComparer.EndsWith }; if(mask.Contains(comparer) && left.Type != typeof(string)) { comparer = OperatorComparer.Equals; } if(!mask.Contains(comparer)) { return Expression.MakeBinary((ExpressionType)comparer,left,Expression.Convert(right,left.Type)); } return BuildStringCondition(left,right); } private static Expression BuildStringCondition(Expression left,Expression right) { var compareMethod = typeof(string).GetMethods().Single(m => m.Name.Equals(Enum.GetName(typeof(OperatorComparer),comparer)) && m.GetParameters().Count() == 1); //we assume ignoreCase,so call ToLower on paramter and memberexpression var toLowerMethod = typeof(string).GetMethods().Single(m => m.Name.Equals("ToLower") && m.GetParameters().Count() == 0); left = Expression.Call(left,toLowerMethod); right = Expression.Call(right,toLowerMethod); return Expression.Call(left,compareMethod,right); } private static Expression MakeLambda(Expression parameter,Expression predicate) { var resultParameterVisitor = new ParameterVisitor(); resultParameterVisitor.Visit(parameter); var resultParameter = resultParameterVisitor.Parameter; return Expression.Lambda(predicate,(ParameterExpression)resultParameter); } private class ParameterVisitor : ExpressionVisitor { public Expression Parameter { get; private set; } protected override Expression VisitParameter(ParameterExpression node) { Parameter = node; return node; } } } } 这可以像 var predicate = ExpressionBuilder.BuildPredicate<Books>("Heading",OperatorComparer.Equals,"Page","Heading"); query = query.Where(predicate); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |