【问题标题】:LINQ to Entities string based dynamic WhereLINQ to Entities 基于字符串的动态 Where
【发布时间】:2016-06-30 20:14:39
【问题描述】:

我有一个使用 Kendo Grid 的 MVC 站点,我正在尝试实现动态过滤器。我显示的数据包含几个一对多表。例如,我有一排人,每个人可以有 0 个或多个 Item 分配给他们。我在网格中显示一个扁平列表:

Bob  | Item 1, Item 2
Jane | Item 3

如果我在 Items 列上硬编码过滤器,它看起来像:

people.Where(p=> p.Items.Any(i=>i.name.contains("Item 1"))).ToList()

我想提出一种通用的方法来构建表达式树,这样我就可以过滤不同的一对多字段并执行不同的比较(例如包含、起始、等于等)。理想情况下,我会有一个具有以下语法的扩展方法:

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 方法。任何想法将不胜感激。

编辑 - 我的代码在一个封闭的网络上,所以我会尽力复制我正在尝试的内容。这是我试图修改的代码。评论区是我卡住的地方。上面调用“Where”扩展方法的例子仍然有效。

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

【问题讨论】:

  • 使用Dynamic LINQ library 怎么样?您必须检查它提供的功能。也许它对您有帮助。您可以针对任何 LINQ 数据提供程序(包括 LINQ to SQL、LINQ to Objects、LINQ to XML、LINQ to Entities、LINQ to SharePoint、LINQ to TerraServer 等)使用 DynamicQuery 库。
  • 你说你的问题是让 GenerateSelector 方法工作,但是你没有向我们展示你遇到问题的代码。如果您对某些代码有疑问,您必须向我们展示代码,否则我们无法帮助您。

标签: c# entity-framework expression-trees


【解决方案1】:

我尝试使用基于 LINQ to Entities 字符串的动态 OrderBy 作为起点,因为概念相似,但我不知道如何修改 GenerateSelector 方法。

期望 selector 的方法(例如 SelectOrderByThenBy 等)与期望 predicate 的方法(例如 @987654324)之间存在显着差异@、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);
}

【讨论】:

  • 非常感谢您的示例。它回答了我所有的问题。
猜你喜欢
  • 2012-02-25
  • 1970-01-01
  • 1970-01-01
  • 2013-01-15
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
  • 1970-01-01
  • 2011-04-03
相关资源
最近更新 更多