Somente inicializadores, membros da entidade e propriedades de navegação da entidade são compatíveis

102

Estou recebendo esta exceção:

O membro do tipo especificado 'Pago' não é compatível com LINQ to Entities. Apenas inicializadores, membros da entidade e propriedades de navegação da entidade são suportados.

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .Where(o => o.Paid == false)
            .OrderByDescending(o => o.DateCreated);

        return View(debts);
    }

Minha classe de modelo

public partial class Order
{
    public bool Paid {
        get {
            return TotalPaid >= Total;
        }
    }

    public decimal TotalPaid {
        get {
            return Payments.Sum(p => p.Amount);
        }
    }

Pagamentos é uma tabela Relacionada contendo o campo valor, A consulta funciona se eu remover a cláusula Where mostrando informações corretas sobre os pagamentos, alguma pista do que há de errado com o código?

Resolvido como a resposta sugerida com:

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .OrderByDescending(o => o.DateCreated)
            .ToList()
            .Where(o => o.Paid == false);

        return View(debts);
    }
Marc
fonte
15
Resposta simples: você não pode usar propriedades não mapeadas em consultas linq para entidades! Apenas as propriedades mapeadas são convertidas em SQL.
Ladislav Mrnka

Respostas:

114

A entidade está tentando converter sua propriedade Paga em SQL e não consegue porque ela não faz parte do esquema da tabela.

O que você pode fazer é deixar a Entidade consultar a tabela sem filtro pago e, em seguida, filtrar os não pagos.

public ActionResult Index()
{
    var debts = storeDB.Orders
        //.Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

    debts = debts.Where(o => o.Paid == false);

    return View(debts);
}

Isso, é claro, significaria trazer todos os dados de volta para o servidor da web e filtrar os dados nele. Se você deseja filtrar no servidor de banco de dados, pode criar uma coluna calculada na tabela ou usar um procedimento armazenado.

Eugene S.
fonte
25

Só tive que resolver um problema semelhante. As soluções acima requerem processamento na memória, o que é uma prática ruim (carregamento lento).

Minha solução foi escrever um auxiliar que retornasse um predicado:

public static class Extensions
{
    public static Expression<Func<Order, bool>> IsPaid()
    {
        return order => order.Payments.Sum(p => p.Amount) >= order.Total;
    }
}

Você pode reescrever sua instrução linq como:

var debts = storeDB.Orders
                    .Where(Extensions.IsPaid())
                    .OrderByDescending(o => o.DateCreated);

Isso é útil quando você deseja reutilizar a lógica de cálculo (DRY). A desvantagem é que a lógica não está em seu modelo de domínio.

Koen Luyten
fonte
1
Existem várias bibliotecas que tentam tornar essa abordagem mais "integrada", consulte: stackoverflow.com/a/27383641/470183 . Linq-para-entidades é limitado a expressões que usam "Funções Canônicas" - que podem ser transformadas em SQL. C # 6 introduziu "funções corporais de expressão", mas essas não são lambdas verdadeiras (consulte: stackoverflow.com/a/28411444/470183 ). Ainda assim, seria bom ter isso na estrutura, portanto, o WIBNI data.uservoice.com/forums/…
James Close
1
Obrigado por este exemplo simples e conciso de Expression<Func<xx,yy>>. Eu já entendi isso antes, mas parece óbvio agora.
AlexB
17

Esse problema também pode vir de uma [NotMapped]propriedade que tem o mesmo nome em seu modelo de banco de dados e modelo de exibição.

O AutoMapper tenta selecioná-lo do banco de dados durante uma projeção; e a propriedade NotMapped obviamente não existe no DB.

A solução é Ignorea propriedade na configuração do AutoMapper ao mapear do Modelo de BD para o Modelo de Visualização.

  1. Procure uma [NotMapped]propriedade com nome Fooem seu modelo de banco de dados.
  2. Procure uma propriedade com o mesmo nome,, Fooem seu modelo de visualização.
  3. Se for esse o caso, altere a configuração do AutoMapper. Adicionar.ForMember(a => a.Foo, b => b.Ignore());
Jess
fonte
Projeção Dang AutoMapper me pegou também, obrigado pela resposta!
Chase Florell
15

Linq converte as instruções em instruções SQL e as executa no banco de dados.

Agora, essa conversão ocorre apenas para membros de entidades, inicializadores e propriedades de navegação de entidade. Portanto, para alcançar a função ou obter a comparação de propriedades, precisamos primeiro convertê-los em uma listagem na memória e, em seguida, aplicar a função para recuperar os dados.

Portanto, na totalidade,

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);
T Gupta
fonte
21
Eu sugeriria que pedir a alguém para fazer um toList () nos pedidos é perigoso, pois isso significaria recuperar a lista inteira
elgrego
Isso é bom para mim porque minha propriedade problemática está em uma função Sum Linq e não na cláusula Where. Portanto, não estou recebendo dados desnecessários e, nos dados recuperados, estou fazendo a função Linq Sum que está trabalhando na Lista. Obrigado! O que pode parecer ruim no início pode ser muito útil em certas situações!
Dov Miller de
11

O outro motivo provável é porque você está usando IEnumerable para sua propriedade, em vez deICollection

Então, em vez de:

public class This
{
    public long Id { get; set; }
    //...
    public virtual IEnumerable<That> Thats { get; set; }
}

Faça isso:

public class This
{
    public long Id { get; set; }
    //...
    public virtual ICollection<That> Thats { get; set; }
}

E você está ótimo ... coisa estúpida para perder 2 horas.

Serj Sagan
fonte
2

Essa situação também pode acontecer se você estiver usando tipos não suportados por EntityFramework , como unsigned int.

Este foi o meu caso de tal erro.

Confira mais informações sobre os tipos suportados: https://msdn.microsoft.com/en-us/library/ee382832(v=vs.100).aspx

Existem algumas soluções alternativas para tais situações, explicadas por GFoley83: Como usar tipos int / longos sem sinal com o Entity Framework?

Ony
fonte
Este link economizou muito tempo! Muito obrigado!
Vladimir Semashkin
0

Eu enfrentei esse problema porque tinha uma variável de membro com apenas get without setpropriedade

isso significa que é auto calculatede not storedcomo uma coluna emthe table

portanto está not existnotable schema

de modo make sureque qualquer variável membro not auto calculatedde haveum gettere setterpropriedades

Basheer AL-MOMANI
fonte
-1

seu edmx e o modelo de contexto possuem algumas propriedades diferentes que foram adicionadas ao banco de dados.

Atualize seu EDMX, atualize-o corretamente Bulid seu projeto e execute novamente.

Isso resolverá seu problema.

Atenciosamente, Ganesh Nikam

Ganesh Nikam
fonte