Eu tenho o seguinte método de extensão 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;
}
Infelizmente, o Entity Framework não sabe como lidar com o, predicate
já que C # converteu o predicado para o seguinte:
e => ((IEntity)e).Id == id
Entity Framework lança a seguinte exceção:
Não é possível lançar o tipo 'IEntity' para 'SomeEntity'. O LINQ to Entities só oferece suporte à conversão de tipos primitivos ou de enumeração de EDM.
Como podemos fazer o Entity Framework funcionar com nossa IEntity
interface?
Algumas explicações adicionais sobre a
class
"correção".Esta resposta mostra duas expressões diferentes, uma com e outra sem
where T: class
restrição. Sem aclass
restrição, temos:e com a restrição:
Essas duas expressões são tratadas de maneira diferente pela estrutura da entidade. Olhando para as fontes do EF 6 , pode-se descobrir que a exceção vem daqui, veja
ValidateAndAdjustCastTypes()
.O que acontece é que EF tenta lançar
IEntity
em algo que faça sentido no mundo do modelo de domínio, mas falha em fazer isso, portanto, a exceção é lançada.A expressão com a
class
restrição não contém oConvert()
operador, o cast não é tentado e está tudo bem.Ainda permanece a questão em aberto, por que LINQ constrói expressões diferentes? Espero que algum assistente C # seja capaz de explicar isso.
fonte
Entity Framework não oferece suporte para isso fora da caixa, mas um
ExpressionVisitor
que traduz a expressão é facilmente escrito:A única coisa que você terá que fazer é converter o predicado passado usando a expressão visitor da seguinte maneira:
Outra abordagem menos flexível é fazer uso de
DbSet<T>.Find
:fonte
Eu tive o mesmo erro, mas um problema semelhante, mas diferente. Eu estava tentando criar uma função de extensão que retornasse IQueryable, mas os critérios de filtro eram baseados na classe base.
Acabei encontrando a solução que meu método de extensão deveria chamar .Select (e => e as T), onde T é a classe filha ee é a classe base.
os detalhes completos estão aqui: Crie a extensão IQueryable <T> usando a classe base no EF
fonte