IQueryable <T> с EntityObject, используя Generics & Interfaces (возможно?)
У меня есть поисковый репозиторий для EntityFramework 4.0 с использованием LinqKit со следующей функцией поиска:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : EntityObject
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
И еще один класс, который использует возвращаемое значение IQueryable для поднабора запроса способами, которые невозможны с помощью логических выражений LinqKit PredicateBuilder:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc);
}
Проблема здесь в том, что «T» как EntityObject не определяет GUID, поэтому я не могу использовать это. Естественным ответом на это является фактическое определение метода SubsetByUser () для использования ограничения со свойством GUID:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : IHaveMetadata
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc);
}
Но это не работает. Я использую LinqKit и метод Expandable () приводит к:
System.NotSupportedException: Unable to cast the type 'Oasis.DataModel.Arc' to
type 'Oasis.DataModel.Interfaces.IHaveMetadata'. LINQ to Entities only supports
casting Entity Data Model primitive types
Мне нужен IQueryable для возвращения. Я могу сделать подделку, как это:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.AsEnumerable()
.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GUID,
meta => meta.ElementGUID,
(arc, meta) => arc)
.AsQueryable();
}
Что, конечно, работает, но это также, конечно, безумное занятие. (Единственная причина, по которой я хочу IQueryable - не выполнять запрос до тех пор, пока мы не закончим.
Я даже попробовал это:
public IQueryable<T> SubsetByUser<T>(IQueryable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
meta => meta.ElementGUID,
(arc, meta) => arc);
}
Который использует отражение, чтобы получить коллекцию Runs - обойти ошибку компилятора. Я думал, что это было довольно умно, но это приводит к исключению LINQ:
System.NotSupportedException: LINQ to Entities does not recognize the
method 'System.Object GetValue(System.Object, System.Object[])' method,
and this method cannot be translated into a store expression.
Я также мог бы попытаться изменить метод поиска:
public IQueryable<T> Search<T>(Expression<Func<T, bool>> predicate)
where T : IRunElement
{
return _unitOfWork.ObjectSet<T>().AsExpandable().Where(predicate);
}
Но это, конечно, не скомпилируется, потому что IRunElement не является EntityObject, а ObjectSet ограничивает T как класс.
Последняя возможность - просто сделать все параметры и возвращаемые значения IEnumerable:
public IEnumerable<T> SubsetByUser<T>(IEnumerable<T> set, User user)
where T : EntityObject
{
return set.Join(_searcher.Search<Metadatum>((o) => o.UserGUID == user.GUID),
arc => arc.GetType().GetProperty("GUID").GetValue(arc,null),
meta => meta.ElementGUID,
(arc, meta) => arc);
}
Что также работает, но, опять же, не позволяет нам откладывать создание экземпляров до конца.
Таким образом, кажется, что я мало что могу сделать, чтобы сделать эту работу, не создавая все экземпляры как IEnumerable, а затем возвращая его с помощью AsQueryable (). Есть ли какой-нибудь способ, которым я могу собрать это воедино, что мне не хватает?