简体   繁体   English

动态Lambda表达式在LINQ to EF Where函数中不起作用

[英]Dynamic lambda expression doesn't work in LINQ to EF Where function

I'm trying to accomplish a page in asp.net mvc to filter a sql server view. 我正在尝试在asp.net mvc中完成一个页面以过滤sql server视图。 My page looks like this: 我的页面如下所示: 在此处输入图片说明

Each "filter" is a SearchViewModel which has a class definition like this: 每个“过滤器”都是一个SearchViewModel ,它具有如下的类定义:

public class SearchViewModel
{
   //Property has a property called "SqlColumnName"
   public Property Property { get; set; }
   public Enums.SearchOperator Operator { get; set; }
   public string Value { get; set; }
}

When I submit the form a IList<SearchViewModel> will be passed to the controller. 当我提交表单时, IList<SearchViewModel>将被传递给控制器​​。 Until here everything works fine. 到这里为止,一切正常。

In the controller class I'm passing the IList<SearchViewModel> to a helper method: 在控制器类中,我将IList<SearchViewModel>传递给辅助方法:

public static Func<ViewEvents, bool> GetLamdaExpression(IEnumerable<SearchViewModel> lstSearchViewModels)
{
    Expression dynamiclambda = null;
    Expression call;

    //creating the first part of the lambda expression (select.Where(ve => ve.Name == "exp"))
    // this function returns "(ve =>" as typeof ViewEvents
    var param = Expression.Parameter(typeof(ViewEvents), "viewEvents");

    //storing functions for use with the combiner
    var containsMethod = typeof(string).GetMethod("Contains");
    var equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
    var startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
    var endsWithMethod = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
    var toLowerMethod = typeof(string).GetMethod("ToLower", Type.EmptyTypes);

    foreach (var lstSearchViewModel in lstSearchViewModels.Where(svm => svm.Value != null))
    {
        //get the property info of the property from the SearchViewModel
        var property = typeof(ViewEvents).GetProperty(lstSearchViewModel.Property.SqlColumnName);
        if (property == null)
            throw new ObjectNotFoundException($"The object {typeof(ViewEvents).ToString()} does not have a property called {lstSearchViewModel.Property.SqlColumnName}");

        //create the second part of the lambda expression
        //this function returns "ve.Property"
        var propertyAccess = Expression.MakeMemberAccess(param, property);

        //add the "toLower" function to the property
        //this function returns "(ve.Property.ToLower()"
        var toLower = Expression.Call(propertyAccess, toLowerMethod);

        //adds the operator to the lambda expression
        //functions return p.ex.: ve.Property.ToLower().Contains("value")
        //                     or ve.Property.ToLower().Equals("value") != true  (NotEquals)
        switch (lstSearchViewModel.Operator)
        {
            case Enums.SearchOperator.Contains:
                call = Expression.Call(toLower, containsMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                break;
            case Enums.SearchOperator.ContainsNot:
                call = Expression.Call(toLower, containsMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                //adding ..Contains("value") != true ; used like .ContainsNot("value")
                call = Expression.NotEqual(call, Expression.Constant(true));
                break;
            case Enums.SearchOperator.StartsWith:
                call = Expression.Call(toLower, startsWithMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                break;
            case Enums.SearchOperator.EndsWith:
                call = Expression.Call(toLower, endsWithMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                break;
            case Enums.SearchOperator.Equals:
                call = Expression.Call(toLower, equalsMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                break;
            case Enums.SearchOperator.EqualsNot:
                call = Expression.Call(toLower, equalsMethod,
                    Expression.Constant(lstSearchViewModel.Value.ToLower()));
                //adding ..Equals("value") != true ; used like .NotEquals("value")
                call = Expression.NotEqual(call, Expression.Constant(true));
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
        //Combind the filters with an and combiner
        dynamiclambda = dynamiclambda == null ? call : Expression.And(dynamiclambda, call);
    }

    if (dynamiclambda == null)
    {
        throw new InvalidOperationException("No dynamiclambda was created");
    }

    //gets the actual lambda expression like: (ve => ve.Property.ToLower().Contains("value") AND ve.Property.ToLower().Equals("value") ...
    var predicate = Expression.Lambda<Func<ViewEvents, bool>>(dynamiclambda, param);
    var compiled = predicate.Compile();

    return compiled;
}

Now when I enter a value of "1" in the input field next to "Typ", the variable predicate of the function contains the following value: 现在,当我在“ Typ”旁边的输入字段中输入值“ 1”时,该函数的变量predicate包含以下值:

{viewEvents => viewEvents.Level.ToLower().Contains("1")}

Later on in the controller the compiled predicate will be used to query the view of the database with entity framework 6 like this: 稍后在控制器中,将使用编译后的谓词通过实体框架6查询数据库的视图,如下所示:

var lambda = Helper.GetLamdaExpression(lstSearchViewModels);
var eventEntries = DataBase.ViewEvents.Where(lambda);

and here comes the "error". 这就是“错误”。 No rows were returned from the database. 没有从数据库返回任何行。 But when I change the code to 但是当我将代码更改为

var lambda = Helper.GetLamdaExpression(lstSearchViewModels);
var eventEntries = DataBase.ViewEvents.Where(viewEvents => viewEvents.Level.ToLower().Contains("1"));

The expected number of rows are returned. 返回预期的行数。

Anyone an idea where the bug is? 任何人都知道错误在哪里吗?

The problem is that you must use an Expression<Func<>> and not a Func<> . 问题是您必须使用Expression<Func<>>而不是Func<> The first contains an expression tree that can be parsed to convert the Where clause into SQL. 第一个包含一个表达式树,可以对其进行解析以将Where子句转换为SQL。 This can't be done with the latter. 后者无法做到。

Besides you're overcomplicating: you can return a lambda directly, without doing so much work. 除了您过于繁琐之外:您可以直接返回lambda,而无需做太多工作。 This is a simplified example: 这是一个简化的示例:

public Expression<Func<ViewEvents, bool>> GetEqualIdLambda(int id)
{
    return (ViewEvents ve) => ve.Id == id;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM