Создание предиката многократного использования для EntitySet <T>, IQueryable <T> и IEnumerable <T>
В моей установке LINQ to SQL у меня есть разные таблицы, которые сопоставляются классам, которые в основном поддерживают один и тот же интерфейс для поддержки версий, т.е.
public interface IValid
{
int? validTo { get; }
int validFrom { get; }
}
Классы LINQ to SQL получают этот интерфейс следующим образом:
public partial class representationRevision : IValid
{
}
Теперь я хотел бы определить способ фильтрации DRY (Do not Repeat Yourself) 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(), но это создает исключение. Undeterred Я попытался сделать предикат, чтобы использовать метод 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
Не уверен в EntitySet<T>
, поэтому сосредоточимся на IQueryable<T>
и IEnumerable<T>
.
Чтобы поставщик LINQ оценивал деревья выражений, которые IQueryable<T>
использует для своих аргументов выражения, необходимо обеспечить, чтобы базовый интерфейс сохранялся. В противном случае сделайте все в рамках IEnumerable<T>
(т.е. Если вы в порядке, чтобы вытащить весь набор данных в локальную память, а затем обработайте его там, а не в базе данных, просто используйте LINQ для объектов).
Другой вариант - это метод расширения 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();
}
Итак, у вас есть общий код с адаптером.