我有一个利用Kendo Grid的MVC网站,我试图实现动态过滤器。我显示的数据包含几个1对多表。例如,我有一排人,每个人可以分配0个或更多项。我在网格中显示一个扁平列表:
Bob | Item 1, Item 2
Jane | Item 3
如果我要在“项目”列上对过滤器进行硬编码,它将如下所示:
people.Where(p=> p.Items.Any(i=>i.name.contains("Item 1"))).ToList()
我想提出一种构建表达式树的通用方法,这样我就可以过滤不同的1对多字段并执行不同的比较(例如contains,startswith,equals等)。理想情况下,我会使用以下语法的扩展方法:
public static IQueryable<TEntity> Where( this IQueryable<TEntity> source, string tableName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class
然后我可以查询多个一对多表:
if(searchOnItems)
persons = persons.Where("Items", "name", "Contains", "item 1);
if(searchOnOtherTableName)
persons = persons.Where("OtherTableName", "name", "Equals", "otherSearchValue);
persons.ToList();
我尝试使用LINQ to Entities string based dynamic OrderBy作为起点,因为概念类似,但我无法弄清楚如何修改GenerateSelector方法。任何想法都将不胜感激。
编辑 - 我的代码位于封闭的网络上,因此我会尽我所能复制我正在尝试的内容。这是我尝试修改的代码。评论栏是我被困的地方。调用&#34; Where&#34;的例子上面的扩展方法仍然有效。
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string tableName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class
{
MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, "Where", tableName, fieldName, comparisonOperator, searchVal);
return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
}
private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, string tableName, String fieldName, string comparisonOperator, string searchVal) where TEntity : class
{
Type type = typeof(TEntity);
Type selectorResultType;
LambdaExpression selector = GenerateSelector<TEntity>(tableName, fieldName, comparisonOperator, searchVal, out selectorResultType);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
new Type[] { type, selectorResultType },
source.Expression, Expression.Quote(selector));
return resultExp;
}
private static LambdaExpression GenerateSelector<TEntity>(string tableName, String fieldName, string comparisonOperator, string searchVal, out Type resultType) where TEntity : class
{
// Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField).
var parameter = Expression.Parameter(typeof(TEntity), "Entity");
PropertyInfo property = typeof(TEntity).GetProperty(tableName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);;
Expression propertyAccess = Expression.MakeMemberAccess(parameter, property);;
/************************************************/
//property is now "TEntity.tableName"
//how do I go another step further so it becomes "TEntity.tableName.comparisonOperator(searchVal)"
/************************************************/
resultType = property.PropertyType;
// Create the order by expression.
return Expression.Lambda(propertyAccess, parameter);
}
答案 0 :(得分:3)
我正在尝试使用LINQ to Entities基于字符串的动态OrderBy作为起点,因为概念类似,但我无法弄清楚如何修改GenerateSelector方法。
期望选择器的方法(如Select
,OrderBy
,ThenBy
等与期望谓词<的方法之间存在显着差异/ strong>喜欢Where
,Any
等。后者不能使用上面的GenerateMethodCall
,因为它假设有2个泛型参数(new Type[] { type, selectorResultType }
),而谓词方法只使用1个泛型参数。
以下是如何实现目标的方法。我试图以某种方式制作它,以便您可以按照表达式构建的每一步进行操作。
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string collectionName, string fieldName, string comparisonOperator, string searchVal) where TEntity : class
{
var entity = Expression.Parameter(source.ElementType, "e");
var collection = Expression.PropertyOrField(entity, collectionName);
var elementType = collection.Type.GetInterfaces()
.Single(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.GetGenericArguments()[0];
var element = Expression.Parameter(elementType, "i");
var elementMember = Expression.PropertyOrField(element, fieldName);
var elementPredicate = Expression.Lambda(
GenerateComparison(elementMember, comparisonOperator, searchVal),
element);
var callAny = Expression.Call(
typeof(Enumerable), "Any", new[] { elementType },
collection, elementPredicate);
var predicate = Expression.Lambda(callAny, entity);
var callWhere = Expression.Call(
typeof(Queryable), "Where", new[] { entity.Type },
source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery<TEntity>(callWhere);
}
private static Expression GenerateComparison(Expression left, string comparisonOperator, string searchVal)
{
var right = Expression.Constant(searchVal);
switch (comparisonOperator)
{
case "==":
case "Equals":
return Expression.Equal(left, right);
case "!=":
return Expression.NotEqual(left, right);
}
return Expression.Call(left, comparisonOperator, Type.EmptyTypes, right);
}