Метод расширения LINQ для создания запроса Where

Я пытаюсь создать метод расширения, который можно будет использовать как для LINQ-to-Object, так и для LINQ-to-Entities для создания функционирующего запроса Where. В конце концов, в него войдет больше, но для начала у меня возникла проблема с получением метода для выбора столбца лямбда и использования его в качестве основы для вызова Contains(). Мне удалось заставить что-то работать для LINQ-to-Objects, но когда я пытаюсь использовать это для LINQ-to-Entities, возникает проблема.

Вот что работает для LINQ-to-Objects:

public static IQueryable<T> WhereContains<T>(this IQueryable<T> query, Expression<Func<T, string>> column, IList<string> values)
{
    return query.Where(o => values.Contains(column.Compile().Invoke(o)));
}

Если это выполняется против Entity Framework, я получаю исключение с указанием

LINQ to Entities не распознает метод...

У меня есть ощущение, что это потребует использования ExpressionVisitor, но я не смог понять, как его нужно подключить. Кто-нибудь смог это сделать?

Обновлять:

Я бы сказал, что это не дубликат, поскольку ответ, предоставленный xanatos, показал прямой способ сделать это без использования ExpressionVisitor.


person StuffOfInterest    schedule 16.07.2018    source источник
comment
Вы правы, что-то подобное нужно. LINQKit .AsExpandable() будет работать здесь. Об этом уже спрашивали и отвечали раньше, позвольте мне посмотреть, смогу ли я найти ответ, который будет вдаваться в подробности.   -  person    schedule 16.07.2018
comment
Я думаю, что o. там быть не должно (o => o.values... должно быть o => values...)   -  person Rafalon    schedule 16.07.2018
comment
Вы правы в дополнительном о.. Я переписывал между системами. Исправил пример выше.   -  person StuffOfInterest    schedule 16.07.2018
comment
@xanatos Каким образом вопрос не является дубликатом вопроса, дубликатом которого он был закрыт?   -  person Servy    schedule 16.07.2018
comment
@Servy Другой вопрос был о композиции двух выражений ... Это всего лишь вставка одного в другое. Кажется, единственной общей частью является использование Contains в качестве метода. Если вы посмотрите на ответ на другой вопрос, он должен использовать ExpressionVisitor.   -  person xanatos    schedule 16.07.2018
comment
@Servy Другой пост хотел получить x.PossibleSubPath.MyStringProperty.Contains("some literal"), этот хочет содержать x => someCollection.Contains(x.Property)   -  person xanatos    schedule 16.07.2018
comment
@xanatos Это буквально составление двух выражений. Мало того, что они оба спрашивают, как составить два выражения, они даже пытаются составить их так, чтобы они делали почти одно и то же. То, что есть несколько тривиальных различий в именах свойств и других совершенно не относящихся к делу деталях относительно того, что делают фактические выражения, совершенно не имеет значения. Они все еще сочиняются. Каждый отдельный вопрос о том, как составить два выражения, не является дубликатом только потому, что они имеют разные имена свойств в своих выражениях. Это абсурд.   -  person Servy    schedule 16.07.2018
comment
@Servy Другая проблема заключается в том, что в принятом ответе используется Expression.Invoke, что часто плохо сочетается с ORM (которые помечены в этом вопросе).   -  person xanatos    schedule 16.07.2018
comment
@xanatos Я вижу, ты на самом деле не читал ответ. Пожалуйста, прочитайте ответ, прежде чем комментировать его дальше.   -  person Servy    schedule 16.07.2018
comment
@Servy Да, вы использовали ExpressionVisitor в качестве второго варианта... Я не заметил, что ответ был твой. Этот ответ, вероятно, является более общим случаем этого вопроса. Там есть два переменных выражения, которые нужно составить (и на самом деле я не думаю, что вы можете сделать это так же легко, как здесь). Здесь есть только одно переменное выражение и одно фиксированное выражение.   -  person xanatos    schedule 16.07.2018
comment
@xanatos То, что делают выражения, не имеет значения. Вам нужно составить одно с другим. Метод делает именно это для любых двух произвольных выражений безоговорочно. Все, что вам нужно сделать, это составить их. Вот и все. Вы сделаете это точно так же, как и для двух других выражений для другого вопроса, потому что единственное отличие состоит в некоторых второстепенных именах свойств, которые вообще не влияют на решение.   -  person Servy    schedule 16.07.2018
comment
@Servy Ваше решение - более сложное решение более сложной проблемы. Мое решение является более простым решением для подмножества всей проблемы. 3 ряд против 15-20.   -  person xanatos    schedule 16.07.2018
comment
@xanatos То, что вы думаете, что можете предложить лучшее решение, не делает вопросы не дублирующими. Кроме того, строки кода — очень плохая замена простому решению. Помимо многократно используемого библиотечного метода, который нужно просмотреть только один раз (если вообще нужно), мое решение создает однострочное полностью статически типизированное решение для каждой возможной перестановки проблемы. Это значительно упрощает написание и чтение, а также устраняет многочисленные возможности для ошибок. Но опять же, если вы думаете, что можете дать лучший ответ на дубликат, это не делает вопросы не дубликатами.   -  person Servy    schedule 16.07.2018


Ответы (1)


Да, вам нужно немного изменить Expression, но вам не нужен ExpressionVisitor. Это намного проще.

public static IQueryable<TSource> WhereContains<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> column, IList<TResult> values)
{
    MethodInfo iListTResultContains = typeof(ICollection<>).MakeGenericType(typeof(TResult)).GetMethod("Contains", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(TResult) }, null);

    var contains = Expression.Call(Expression.Constant(values), iListTResultContains, column.Body);

    var lambda = Expression.Lambda<Func<TSource, bool>>(contains, column.Parameters);

    return query.Where(lambda);
}

Обратите внимание, что я немного расширил его, чтобы охватить более string.

person xanatos    schedule 16.07.2018
comment
Отлично, спасибо. Я не могу использовать общий результат из-за других ограничений, связанных с тем, что я делаю с данными внутри метода, но пример, который вы предоставляете, синтаксис. Мне нужно больше покопаться в помощниках по построению выражений. - person StuffOfInterest; 16.07.2018