在 LinqToEntities 中,如何将动态列名传递给 DbFunctions.Like

Posted

技术标签:

【中文标题】在 LinqToEntities 中,如何将动态列名传递给 DbFunctions.Like【英文标题】:In LinqToEntities, How to pass dynamic column name to DbFunctions.Like 【发布时间】:2020-08-01 06:28:54 【问题描述】:

我在 Entity Framework 中有一个来自 DbSetIQueryable<T>。为我提供了一个名为 searchText 的“模糊搜索字符串”,如下所示:

public List<T> Search<T>(string searchText)

    using (var context = ...)
    
        var baseQuery = context.Set<T>().AsQueryable();
        baseQuery = baseQuery.Where(x =>
            DbFunctions.Like(x.PropertyName, searchText)
            || DbFunctions.Like(x.PropertyTwo, searchText)
            || DbFunctions.Like(x.PropertyThree, searchText)
            || DbFunctio..... etc
        );
        return baseQuery.ToList();
    

但鉴于通用性质,我不知道该类型有哪些属性。我可以为实现它的人提供一个抽象方法,使他们可以给我一个属性列表(甚至PropertyInfo 或其他任何东西,我可以弄清楚)。但我不知道如何动态创建表达式。这是我目前所拥有的:

var baseQuery = context.Set<T>().AsQueryable();
var expression = baseQuery.Expression;
var colName = "colName"; // Or names, I can iterate.

var parameter = Expression.Parameter(typeof(T), "x");
var selector = Expression.PropertyOrField(parameter, colName);
expression = Expression.Call(typeof(DbFunctions), nameof(DbFunctions.Like),
    new Type[]  baseQuery.ElementType, selector.Type ,
    expression, Expression.Quote(Expression.Lambda(selector, parameter)));

这里的问题是……好吧,它一开始是行不通的。但主要是我没有在其中的任何地方使用searchText,也不知道如何插入它。我想我已经接近了......但在它上面花费了过多的时间。

【问题讨论】:

【参考方案1】:

希望我的查询逻辑正确:如果您想根据已知类型和列名列表构建一组 LIKE 条件,您可以尝试这样的操作:

static private MethodInfo dbLikeMethod = typeof(DbFunctions).GetMethod(nameof(DbFunctions.Like), BindingFlags.Public | BindingFlags.Static, null, new Type[] typeof(string), typeof(string), null); // I am targeting DbFunctions.Like(string, string). You might want another overload (or even mix them up depending on your inputs)

public List<T> Search<T>(string searchText) where T: class


    using (var context = new ...)
    
        var baseQuery = context.Set<T>().AsQueryable().Where(CreateExpression<T>(searchText));// you could probably find a more elegant way of plugging it into your query
        return baseQuery.ToList();
    


Expression<Func<T, bool>> CreateExpression<T>(string searchText) where T : class
   
    var cols = new List<string> 
        "PropertyName",
        "PropertyTwo" // i understand you've got a way to figure out which strings you need here
    ;

    var parameter = Expression.Parameter(typeof(T), "x");   
    var dbLikeCalls = cols.Select(colName => Expression.Call(dbLikeMethod, Expression.PropertyOrField(parameter, colName), Expression.Constant(searchText))); // for convenience, generate list of DbFunctions.Like(x.<Property>, searchText) expressions here
    var aggregatedCalls = dbLikeCalls.Skip(1).Aggregate((Expression)dbLikeCalls.First(), (accumulate, call) => Expression.OrElse(accumulate, call)); // aggregate the list using || operators: use first item as a seed and keep adding onto it

    return Expression.Lambda<Func<T, bool>>(aggregatedCalls, parameter);

【讨论】:

以上是关于在 LinqToEntities 中,如何将动态列名传递给 DbFunctions.Like的主要内容,如果未能解决你的问题,请参考以下文章

Pentaho:如何将字段(= 列)动态添加到 OutputRow?

如何将新列动态添加到 bigquery 中已存在的表..?

如何将数据库表中的列放入动态列表中?

LINQ to Entities - 在分组数据中创建中值

laravel 模型 - 如何在模型中动态设置列值?

如何在数据表中动态创建列并为其赋值?