C# 在多列中搜索一个术语

C# 在多列中搜索一个术语,c#,entity-framework,linq,linq-to-entities,C#,Entity Framework,Linq,Linq To Entities,我想创建一个扩展来在多个列中搜索术语。 术语用空格分隔,每个术语必须至少显示在一个给定列中 以下是我迄今为止所做的工作: public static IQueryable<TSource> SearchIn<TSource>(this IQueryable<TSource> query, string searchText, Expression<Func<TSource, string>> expression,

我想创建一个扩展来在多个列中搜索术语。 术语用空格分隔,每个术语必须至少显示在一个给定列中

以下是我迄今为止所做的工作:

public static IQueryable<TSource> SearchIn<TSource>(this IQueryable<TSource> query, 
    string searchText, 
    Expression<Func<TSource, string>> expression, 
    params Expression<Func<TSource, string>>[] expressions)
{
    if (string.IsNullOrWhiteSpace(searchText))
    {
        return query;
    }

    // Concat expressions
    expressions = new[] { expression }.Concat(expressions).ToArray();

    // Format search text
    var formattedSearchText = searchText.FormatForSearch();
    var searchParts = formattedSearchText.Replace('\'', ' ').Split(' ');

    // Initialize expression
    var pe = Expression.Parameter(typeof(TSource), "entity");
    var predicateBody = default(Expression);

    // Search in each expressions, put OR in between
    foreach (var expr in expressions)
    {
        var exprBody = default(Expression);

        // Search for each words, put AND in between
        foreach (var searchPart in searchParts)
        {
            // Create property or field expression
            var left = Expression.PropertyOrField(pe, ((MemberExpression)expr.Body).Member.Name);

            // Create the constant expression with current word
            var search = Expression.Constant(searchPart, typeof(string));

            // Create the contains function
            var contains = Expression.Call(left, typeof(string).GetMethod(nameof(string.Contains), new Type[] { typeof(string) }), search);

            // Check if there already a predicate body
            if (exprBody == null)
            {
                exprBody = contains;
            }
            else
            {
                exprBody = Expression.And(exprBody, contains);
            }
        }

        if (predicateBody == null)
        {
            predicateBody = exprBody;
        }
        else
        {
            predicateBody = Expression.OrElse(predicateBody, exprBody);
        }
    }

    // Build the where method expression
    var whereCallExpression = Expression.Call(
        typeof(Queryable),
        nameof(Queryable.Where),
        new Type[] { query.ElementType },
        query.Expression,
        Expression.Lambda<Func<TSource, bool>>(predicateBody, new ParameterExpression[] { pe }));

    // Apply the condition to the query and return it
    return query.Provider.CreateQuery<TSource>(whereCallExpression);
}
但在尝试浏览导航属性时,它不起作用:

// Not working
query.SearchIn("foo", x => x.Nav1.Column1);
这给了我一个例外

“Column1”不是“Nav1”类型的成员

我理解这个问题,但我找不到通过
Nav1
的解决方案


我需要这方面的帮助。

不要解析lambda表达式体,只需使用给定参数调用它:

var left = Expression.Invoke(expr, pe);
但是,它只在EF Core中工作

在EF6中,您需要获得每个嵌套成员的属性或字段,如下所示:

var left = expr.Body.ToString()
    .Split('.')
    .Skip(1) //skip the original parameter name
    .Aggregate((Expression)pe, (a, c) => Expression.PropertyOrField(a, c));
它只适用于简单的lambda,如:

x => x.Prop1.Nav1

如果这还不够,您需要一些更高级的解析算法,例如
ExpressionVisitor

而不是解析lambda表达式体,只需使用给定参数调用它:

var left = Expression.Invoke(expr, pe);
但是,它只在EF Core中工作

在EF6中,您需要获得每个嵌套成员的属性或字段,如下所示:

var left = expr.Body.ToString()
    .Split('.')
    .Skip(1) //skip the original parameter name
    .Aggregate((Expression)pe, (a, c) => Expression.PropertyOrField(a, c));
它只适用于简单的lambda,如:

x => x.Prop1.Nav1

如果这还不够的话,你可能需要一些更高级的解析算法,比如
ExpressionVisitor

你尝试过实现吗?我刚刚尝试过,它也确实非常有效!你试过实现吗?我刚试过,它也确实很有效!使用此方法获得异常:
System.NotSupportedException:“LINQ to实体中不支持LINQ表达式节点类型“Invoke”。
@dbrailon Oh我在EF CoreYeah中测试了它,我使用的是EF6,很高兴知道它与EF core一起工作tho@dbraillon看看我的EF6解决方案,使用这种方法得到了一个异常:
System.NotSupportedException:“LINQ to Entities中不支持LINQ表达式节点类型'Invoke'。
@dbrailon噢,我在EF CoreYeah中测试了它,我在使用EF6,很高兴知道它与EF core一起工作tho@dbraillon看看我的EF6解决方案