Tengo el siguiente método de extensión genérico:
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;
}
Desafortunadamente, Entity Framework no sabe cómo manejar el predicate
ya que C # convirtió el predicado a lo siguiente:
e => ((IEntity)e).Id == id
Entity Framework lanza la siguiente excepción:
No se puede convertir el tipo 'IEntity' para escribir 'SomeEntity'. LINQ to Entities solo admite la conversión de tipos primitivos o de enumeración de EDM.
¿Cómo podemos hacer que Entity Framework funcione con nuestra IEntity
interfaz?
Algunas explicaciones adicionales sobre el
class
"arreglo".Esta respuesta muestra dos expresiones diferentes, una con y otra sin
where T: class
restricción. Sin laclass
restricción tenemos:y con la restricción:
Estas dos expresiones son tratadas de manera diferente por el marco de la entidad. Al observar las fuentes de EF 6 , se puede encontrar que la excepción proviene de aquí, ver
ValidateAndAdjustCastTypes()
.Lo que sucede es que EF intenta
IEntity
convertir algo que tenga sentido en el mundo del modelo de dominio, sin embargo, falla al hacerlo, por lo que se lanza la excepción.La expresión con la
class
restricción no contiene elConvert()
operador, no se prueba la conversión y todo está bien.Todavía queda abierta la pregunta, ¿por qué LINQ crea diferentes expresiones? Espero que algún asistente de C # pueda explicar esto.
fuente
Entity Framework no admite esto de forma inmediata, pero una
ExpressionVisitor
que traduce la expresión se escribe fácilmente:Lo único que tendrá que hacer es convertir el predicado pasado usando la expresión visitante de la siguiente manera:
Otro enfoque, menos flexible, es hacer uso de
DbSet<T>.Find
:fuente
Tuve el mismo error pero un problema similar pero diferente. Estaba tratando de crear una función de extensión que devolviera IQueryable, pero los criterios de filtro se basaron en la clase base.
Finalmente encontré la solución que era para que mi método de extensión llamara .Select (e => e como T) donde T es la clase secundaria ye es la clase base.
los detalles completos están aquí: Cree la extensión IQueryable <T> usando la clase base en EF
fuente