Создание многократно используемого предиката для EntitySet <T>, IQueryable <T>и IEnumerable <T> - PullRequest
12 голосов
/ 27 января 2010

В моей настройке LINQ to SQL у меня есть различные таблицы, которые отображаются на классы, которые в основном поддерживают один и тот же интерфейс для поддержки управления версиями, т.е.

public interface IValid
{
    int? validTo { get; }
    int validFrom { get; }
}

Классы LINQ to SQL наследуются от этого интерфейса следующим образом:

public partial class representationRevision : IValid
{
}

Теперь я хотел бы определить СУХОЙ (не повторяющийся) способ фильтрации EntitySet<T>, IEnumerable<T> и IQueryable<T>, чтобы результирующие списки действовали для конкретной ревизии. Я пытался сделать это:

public static class ExtensionMethods
{

    public static IQueryable<T> ValidFor<T>(this IQueryable<T> v, int? revision)
        where T : IValid
    {
        return v.Where(cr => ((cr.validFrom <= revision) &&
            ((cr.validTo == null) || (cr.validTo > revision)))
            || ((revision == null) && (cr.validTo == null))
            );
    }
}

Но это создает проблемы на EntitySet<T>. Я добавил специальную реализацию для EntitySet, которая сначала вызывает AsQueryable (), но это вызывает исключение. Не пытаясь создать предикат, я мог использовать подход Where(predicate):

    public static Expression<Func<contentRevision, bool>> IsValidFor(int? revision)
    {
        return ((cr) => ((cr.validFrom <= revision) &&
            ((cr.validTo == null) || (cr.validTo > revision)))
            || ((revision == null) && (cr.validTo == null)));
    }

При использовании с .Where<contentRevision>(IsValidFor(revision)) выдает такие ошибки, как:

Ошибка 5 'System.Data.Linq.EntitySet' делает не содержат определения для «Где» и наилучшего метода расширения перегрузки
«System.Linq.Enumerable.Where (System.Collections.Generic.IEnumerable, System.Func) 'имеет некоторые неверные аргументы

Обратите внимание, что это даже без использования интерфейса IValid ... Я пробовал всевозможные варианты этой темы (например, добавление параметра int), но все они неизменно оказываются неудачными. Какие-нибудь указатели, чтобы направить меня в правильном направлении?

1 Ответ

4 голосов
/ 27 января 2010

Не уверен насчет EntitySet<T>, поэтому сосредоточимся на IQueryable<T> и IEnumerable<T>.

Чтобы поставщик LINQ мог оценивать деревья выражений, которые IQueryable<T> использует в качестве аргументов выражения, необходимо обеспечить сохранение базового интерфейса. В противном случае сделайте все с точки зрения IEnumerable<T> (т. Е. Если вы в порядке, чтобы вытащить весь набор данных в локальную память и затем обработать его там, а не в базе данных, просто используйте LINQ to Objects).

Другой вариант - это метод расширения AsQueryable (часть класса Queryable), который преобразует IEnumerable<T> в IQueryable<T>, а затем вы можете поделиться остальным кодом.

IQueryable<T> SomeSharedQuery(this IQueryable<T> source) {
    return source.(LINQ query operators...);
}
IQueryable<T> SomeSharedQuery(this IEnumerable<T> source) {
    return source.AsQueryable().SomeSharedQuery();
}

Итак, у вас есть общий код с методом адаптера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...