微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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);
    }
}
}

Exception Message:-
{“Instance property ‘heading’ is not defined for type >’System.Collections.Generic.List`1[XMLStorageAndFilter.Page]'”}

StackTrace:-
at System.Linq.Expressions.Expression.Property(Expression expression,String propertyName)
at XMLStorageAndFilter.Program2.Main() in c:\Users\Administrator\Documents\Visual Studio 2013\Projects\XMLStorageAndFilter\NavProperty.cs:line 61
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly,String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile,Evidence assemblySecurity,String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,ContextCallback callback,Object state,Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext,Object state)
at System.Threading.ThreadHelper.ThreadStart()

解决方法

您可以使用 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);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐