I have the following generic 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 Entity Framework doesn't know how to handle the predicate
since C# converted the predicate to the following:
e => ((IEntity)e).Id == id
Entity Framework throws the following exception:
Unable to cast the type 'IEntity' to type 'SomeEntity'. LINQ to Entities only supports casting EDM primitive or enumeration types.
How can we make Entity Framework work with our IEntity
interface?
I was able to resolve this by adding the class
generic type constraint to the extension method. I'm not sure why it works, though.
public static T GetById<T>(this IQueryable<T> collection, Guid id)
where T : class, IEntity
{
//...
}
Some additional explanations regarding the class
"fix".
This answer shows two different expressions, one with and the other without where T: class
constraint. Without the class
constraint we have:
e => e.Id == id // becomes: Convert(e).Id == id
and with the constraint:
e => e.Id == id // becomes: e.Id == id
These two expressions are treated differently by the entity framework. Looking at the EF 6 sources, one can find that the exception comes from here, see ValidateAndAdjustCastTypes()
.
What happens is, that EF tries to cast IEntity
into something that makes sense the domain model world, however it fails in doing so, hence the exception is thrown.
The expression with the class
constraint does not contain the Convert()
operator, cast is not tried and everything is fine.
It still remain open question, why LINQ builds different expressions? I hope that some C# wizard will be able to explain this.