[英]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.