如何使 Linq 扩展方法可用于实体?

How can I make Linq extension method available for the entities?

我写了一个扩展 System.Linq class 的方法。我的 ContainsAnyWord 方法允许我针对一个字符串搜索字符串中的所有单词,而不是将一个字符串与另一个字符串进行比较。

下面是我写的扩展方法Linq

public static class LinqExtension
{

    public static bool ContainsAnyWord(this string value, string searchFor)
    {
        if (!string.IsNullOrWhiteSpace(value) && !string.IsNullOrWhiteSpace(searchFor))
        {
            value = value.ToLower();

            IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);

            return tags.Contains(value);

        }

        return false;

    }

}

此扩展方法在 IEnumerable 对象上效果很好。但是当我想在 IQueryable 对象上使用它时,出现以下错误

LINQ to Entities does not recognize the method 'Boolean ContainsAnyWord(System.String, System.String)' method, and this method cannot be translated into a store expression.

以下示例效果很好,因为我使用的是列表。

using(var conn = new AppContext())
{

    var allUsers = conn.Users.GetAll().ToList();
    var foundUsers = allUsers.Where
    (
        user => 
               user.FirstName.ContainsAnyWord("Some Full Name Goes Here")
            || user.LastName.ContainsAnyWord("Some Full Name Goes Here")
    ).ToList();

}

但是下面的示例不起作用,因为我正在使用 IQueryable,这给了我上面列出的错误。

using(var conn = new AppContext())
{

    var allUsers = conn.Users.Where
    (
        user => 
            user.FirstName.ContainsAnyWord("Some Full Name Goes Here") 
         || user.LastName.ContainsAnyWord("Some Full Name Goes Here")
    ).ToList();

}

如何解决此问题并使此方法可用于 IQueryable 对象?

已更新

根据我在下面得到的反馈,我尝试使用 IQueryable 来实现这个

    public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> query, string searchFor)
    {
        if (!string.IsNullOrWhiteSpace(searchFor))
        {
            IEnumerable<string> tags = StopwordTool.GetPossibleTags(searchFor);

            return query.Where(item => tags.Contains(item));

        }

        return query;

    }

但这也给我一个错误

请记住,最后必须将其翻译为 SQL。 显然 ContainsAnyWord 不能翻译成 SQL...
所以,将您的名字保存在 List/Array 中并尝试

 user =>  yourlist.Contains(  user.FirstName)

EF 会将其转换为 WHERE ..IN

不需要方法,但如果出于某种原因你需要它

internal static class MyClass2
{
    public static IQueryable<T> ContainsAnyWord<T>(this IQueryable<T> value, string searchFor) where T: User
    {
        var names = searchFor.Split(' ').ToList();
        return value.Where(u => names.Contains(u.DisplayName));
    }
}

你可以像这样使用它

var result=conn.Users.ContainsAnyWord("abc def ght");

Linq to Entities 将方法转换为数据库表达式。您不能实现 System.String 的扩展并期望它被翻译成 T-SQL 表达式,这是 Entity Framework 内部所做的。

我的建议是尝试使用预定义的 Linq 方法解决问题。尝试使用包含方法。

您不能在查询表达式中调用方法,Linq to Entites 提供的表达式解析器不知道如何处理。您唯一真正能做的就是编写一个扩展方法,该方法接受 IQueryable<T> 并在函数内部构建表达式,而不是调用 Where(

下面的代码完全未经测试,是我突然想到的。但是你需要做这样的事情

public static IQueryable<T> WhereContainsAnyWord<T>(this IQueryable<T> @this, Expression<Func<T, string>> selector, string searchFor)
{
    var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.

    var selectedItems = @this.GroupBy(selector);
    var filteredItems = selectedItems.Where(selectedItem => tags.Contains(selectedItem.Key.ToLower()));
    var result = filteredItems.SelectMany(x => x);
    return result;
}

一样使用
using(var conn = new AppContext())
{

    var allUsers = conn.Users.WhereContainsAnyWord(user => user.FirstName, "Some Full Name Goes Here").ToList();

}

这是一个更简单的非通用版本

public static IQueryable<User> WhereContainsAnyWord(this IQueryable<User> @this, string searchFor)
{
    var tags = StopwordTool.GetPossibleTags(searchFor); //You might need a .ToArray() here.

    return @this.Where(user => tags.Contains(user.DisplayName.ToLower()));
}

一样使用
using(var conn = new AppContext())
{

    var allUsers = conn.Users.WhereContainsAnyWord("Some Full Name Goes Here").ToList();

}