LINQ to Entities only supports EDM listing of primitive or enumerated types with IEntity interface

I have the following general extension method:

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; } 

Unfortunately, the Entity Framework does not know how to handle predicate , since C # converted the predicate to the following:

 e => ((IEntity)e).Id == id 

Entity Framework throws the following exception:

Cannot enter the type "IEntity" for input "SomeEntity". LINQ to Objects only support listing EDM primitives or enumeration types.

How can we make Entity Framework work with our IEntity interface?

+78
c # entity-framework expression-trees dbcontext
Sep 24 '13 at 8:27
source share
4 answers

I managed to solve this problem by adding a class generic type restriction to the extension method. I'm not sure why this works.

 public static T GetById<T>(this IQueryable<T> collection, Guid id) where T : class, IEntity { //... } 
+161
Nov 07 '13 at 22:07
source share

Some additional clarifications regarding the correction of class .

This answer shows two different expressions: one with the other and without limitation where T: class . Without class restrictions, we have:

 e => e.Id == id // becomes: Convert(e).Id == id 

and with the restriction:

 e => e.Id == id // becomes: e.Id == id 

These two expressions are treated differently by the entity. Looking at the sources of EF 6 , you can find that the exception comes from here, see ValidateAndAdjustCastTypes() .

What happens is that EF is trying to distinguish IEntity from something that makes sense in the world of the domain model, but it fails, so the exception is thrown.

An expression with a class constraint does not contain a Convert() operator, the cast is not executed, and everything is fine.

The question still remains open, why is LINQ building different expressions? I hope some C # wizards can explain this.

+57
Jan 08 '14 at 16:06
source share

Entity Framework does not support this out of the box, but ExpressionVisitor , which translates the expression, is easily written:

 private sealed class EntityCastRemoverVisitor : ExpressionVisitor { public static Expression<Func<T, bool>> Convert<T>( Expression<Func<T, bool>> predicate) { var visitor = new EntityCastRemoverVisitor(); var visitedExpression = visitor.Visit(predicate); return (Expression<Func<T, bool>>)visitedExpression; } protected override Expression VisitUnary(UnaryExpression node) { if (node.NodeType == ExpressionType.Convert && node.Type == typeof(IEntity)) { return node.Operand; } return base.VisitUnary(node); } } 

The only thing you need to do is convert the visitor passed into the predicate using the expression as follows:

 public static T GetById<T>(this IQueryable<T> collection, Expression<Func<T, bool>> predicate, Guid id) where T : IEntity { T entity; // Add this line! predicate = EntityCastRemoverVisitor.Convert(predicate); try { entity = collection.SingleOrDefault(predicate); } ... } 

Another flexible approach is to use DbSet<T>.Find :

 // NOTE: This is an extension method on DbSet<T> instead of IQueryable<T> public static T GetById<T>(this DbSet<T> collection, Guid id) where T : class, IEntity { T entity; // Allow reporting more descriptive error messages. try { entity = collection.Find(id); } ... } 
+20
Sep 24 '13 at 8:27
source share

I had the same error, but a similar but different problem. I tried to create an extension function that returned IQueryable, but the filter criteria were based on the base class.

In the end, I found a solution for my extension method: .Select (e => e as T), where T is the child class and e is the base class.

full details here: create an IQueryable <T> extension using the base class in EF

0
Dec 10 '18 at 21:12
source share



All Articles