LINQ to Entities does not recognize the method

LINQ to Entities does not recognize the method 'Boolean TagsCheck(Library.MVVM.Model.Book)' method, and this method cannot be translated into a store expression

Насколько я понял, эта ошибка появляется потому что выражение в запросе не может преобразоваться в sql запрос. И я не могу понять как мне заменить мою функцию на запрос.

Сам запрос

BookList = db.Books.Include("Tags").Where(b => b.Name.Trim().Contains(BookSearch.Trim()) && TagsCheck(b)).ToList();

И функция

bool TagsCheck(Book book)
{
    using (DatabaseContext db = new DatabaseContext())
    {
        foreach (Tag tag in FilterTagList)
            if (!book.Tags.Contains(db.Tags.FirstOrDefault(t => t.TagId == tag.TagId)))
                return false;
        return true;
    }
}

В самом запросе я пытаюсь получить список книг, в список тегов которых, входят теги из списка FilterTagList. Подскажите возможное решение.


Ответы (2 шт):

Автор решения: Blackmeser

Если вы корректно описали ту часть кода, которую вы не показали здесь, касательно строения классов Book и Tag, а так-же BookSearch поля, заведены компараторы на подобии TagComparer, то должно быть достаточно:

class TagComparer : IEqualityComparer<Tag>
{
    public bool Equals(Tag t1, Tag t2)
    {
        if (t1 == null && t2 == null) return true;
        else if (t1 == null || t2 == null) return false;
        else if (t1.TagId == t2.TagId) return true;
        else return false;
    }

    public int GetHashCode(Tag tx)
    {
        return (tx?.TagId ?? 0).GetHashCode();
    }
}

BookList = db.Books
   .Include("Tags")
   .Where(b 
      => b.Name.Contains(BookSearch.Trim()) 
      && b.Tags.Any(a => FilterTagList.Contains(a, new TagComparer())))
   .ToList();
→ Ссылка
Автор решения: Alexander Petrov

Метод TagsCheck должен возвращать Expression<Func<Book, bool>>, чтобы его можно было использовать в linq-запросах Entity Framework.

Насколько я понял, в этом методе проверяется, содержится ли хоть один тег, назначенный книге, в списке FilterTagList.

Этот код можно записать так:

Expression<Func<Book, bool>> TagsCheck()
{
    return book => book.Tags.Any(tag => FilterTagList.Contains(tag));
}

Использовать его следует так:

var books = db.Books.Include("Tags")
    .Where(b => b.Name.Trim().Contains(BookSearch.Trim()))
    .Where(TagsCheck())
    .ToList();

Обратите внимание, его обязательно нужно использовать так: Where(TagsCheck()). Потому что он возвращает Expression! Он не возвращает значение bool!

Его невозможно использовать вот так: .Where(book => TagsCheck()), потому что тогда он конвертируется в делегат.

Он возвращает выражение book => book.Tags.Any(tag => FilterTagList.Contains(tag)) и именно оно будет подставлено в месте вызова.

И, соответственно, такой метод не получится использовать с логическим оператором &&. Отсюда следует отдельный вызов Where.


Метод не принимает параметр, потому что параметр book принимает Expression (выражение). Поэтому, чтобы не возникло соблазна всё-таки попытаться воткнуть какой-нибудь параметр, можно сделать свойство вместо метода:

Expression<Func<Book, bool>> TagsCheck =>
    book => book.Tags.Any(tag => FilterTagList.Contains(tag));

Его использование, естественно, будет без круглых скобочек:

.Where(TagsCheck)
→ Ссылка