简体   繁体   中英

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

I wrote a method that extends System.Linq class. My ContainsAnyWord method allows me to search all words in a string against a string instead of comparing one string to another.

Here is the method that I wrote to extend 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;

    }

}

This extension method works great on an IEnumerable object. But when I want to use it on IQueryable object I get the following error

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.

The following example works great because I am working with a list.

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

}

But the following example does not work because I am working with IQueryable which gives me the error listed above.

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

}

How can I fix this issue and make this method available for IQueryable object?

UPDATED

Based on the feedback I got below, I tried to implement this using IQueryable like so

    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;

    }

But this is also giving me an error

在此处输入图片说明

You have to keep in mind that this has to be translated to SQL at the end. Obviously ContainsAnyWord cannot be translated to SQL...
So ,save your names in a List/Array and try

 user =>  yourlist.Contains(  user.FirstName)

EF will translate this to a WHERE ..IN

There is no need for a method but if for some reason you want it

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

You can use it like

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

You can't call methods inside the query expression the expression parser provided by the Linq to Entites does not know how to handle. The only thing you can really do is write a extension method that takes in a IQueryable<T> and build up the expression internally inside the function instead of calling Where(

This below code is totally untested and is something I came up with off the top of my head. But you would need to do something like this

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

used like

using(var conn = new AppContext())
{

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

}

Here is a simpiler non generic version

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

used like

using(var conn = new AppContext())
{

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

}

Linq to Entities converts Methods into database expressions. You can not implement an extension to System.String and expect it to be translated into a T-SQL expression, wich is what internally Entity Framework does.

My suggestion is trying to work around using pre-defined Linq methods. Try using Contains method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM