У меня есть следующий общий метод расширения:
public static T GetById<T>(this IQueryable<T> collection, Guid id)
where T : IEntity
{
Expression<Func<T, bool>> predicate = e => e.Id == id;
T entity;
// Allow reporting more descriptive error messages.
try
{
entity = collection.SingleOrDefault(predicate);
}
catch (Exception ex)
{
throw new InvalidOperationException(string.Format(
"There was an error retrieving an {0} with id {1}. {2}",
typeof(T).Name, id, ex.Message), ex);
}
if (entity == null)
{
throw new KeyNotFoundException(string.Format(
"{0} with id {1} was not found.",
typeof(T).Name, id));
}
return entity;
}
К сожалению, Entity Framework не знает, как справиться с этим, predicate
поскольку C # преобразовал предикат в следующее:
e => ((IEntity)e).Id == id
Entity Framework выдает следующее исключение:
Невозможно привести тип IEntity к типу SomeEntity. LINQ to Entities поддерживает только приведение примитивов EDM или перечислимых типов.
Как мы можем заставить Entity Framework работать с нашим IEntity
интерфейсом?
Некоторые дополнительные пояснения по поводу
class
"исправления".Этот ответ показывает два разных выражения, одно с
where T: class
ограничением, а другое без ограничения. Безclass
ограничения мы имеем:и с ограничением:
Эти два выражения обрабатываются структурой сущности по-разному. Посмотрев на источники EF 6 , можно обнаружить, что исключение исходит отсюда, см
ValidateAndAdjustCastTypes()
.Происходит то, что EF пытается
IEntity
преобразовать во что-то, что имеет смысл в мире модели предметной области, однако это не удается, поэтому создается исключение.Выражение с
class
ограничением не содержитConvert()
оператора, приведение не выполняется и все в порядке.Остается открытым вопрос, почему LINQ строит разные выражения? Я надеюсь, что какой-нибудь мастер C # сможет это объяснить.
источник
Entity Framework не поддерживает это из коробки, но
ExpressionVisitor
легко написать выражение, которое переводит выражение:Единственное, что вам нужно, это преобразовать переданный в предикат, используя выражение посетитель следующим образом:
Другой - менее гибкий - подход заключается в использовании
DbSet<T>.Find
:источник
У меня была такая же ошибка, но похожая, но другая проблема. Я пытался создать функцию расширения, которая возвращала бы IQueryable, но критерии фильтрации были основаны на базовом классе.
В конце концов я нашел решение, которое было для моего метода расширения для вызова .Select (e => e as T), где T - дочерний класс, а e - базовый класс.
полная информация здесь: Создание расширения IQueryable <T> с использованием базового класса в EF
источник