У меня есть репозиторий поиска для 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(). Есть ли способ, которым я могу собрать это вместе, что мне не хватает?