如何为实体提供Linq扩展方法?

时间:2016-11-10 22:32:42

标签: c# entity-framework linq entity-framework-6 extension-methods

我写了一个扩展System.Linq类的方法。我的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无法识别方法&#39;布尔值   ContainsAnyWord(System.String,System.String)&#39;方法和这种方法   无法翻译成商店表达。

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

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;

    }

但这也给了我一个错误

enter image description here

3 个答案:

答案 0 :(得分:4)

您必须记住,最后必须将其转换为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");

答案 1 :(得分:1)

您无法调用查询表达式中的方法,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();

}

答案 2 :(得分:0)

Linq to Entities将方法转换为数据库表达式。您无法实现System.String的扩展,并期望将其转换为T-SQL表达式,这是内部实体框架所做的。

我的建议是尝试使用预定义的Linq方法。尝试使用Contains方法。