Não é possível criar um valor constante do tipo Apenas tipos primitivos ou tipos de enumeração são suportados neste contexto

164

Estou recebendo este erro para a consulta abaixo

Não foi possível criar um valor constante do tipo API.Models.PersonProtocol. Apenas tipos primitivos ou tipos de enumeração são suportados neste contexto

ppCombinedabaixo é um IEnumerableobjeto de PersonProtocolType, que é construído por concat de 2 PersonProtocollistas.

Por que isso está falhando? Não podemos usar a JOINcláusula LINQ dentro SELECTde a JOIN?

var persons = db.Favorites
    .Where(x => x.userId == userId)
    .Join(db.Person, x => x.personId, y => y.personId, (x, y) =>
        new PersonDTO
        {
            personId = y.personId,
            addressId = y.addressId,                   
            favoriteId = x.favoriteId,
            personProtocol = (ICollection<PersonProtocol>) ppCombined
                .Where(a => a.personId == x.personId)
                .Select( b => new PersonProtocol()
                 {
                     personProtocolId = b.personProtocolId,
                     activateDt = b.activateDt,
                     personId = b.personId
                 })
        });
user2515186
fonte

Respostas:

232

Isso não pode funcionar porque ppCombinedé uma coleção de objetos na memória e você não pode associar um conjunto de dados no banco de dados a outro conjunto de dados que está na memória. Você pode tentar extrair os itens filtrados personProtocolda ppCombinedcoleção na memória depois de recuperar as outras propriedades do banco de dados:

var persons = db.Favorites
    .Where(f => f.userId == userId)
    .Join(db.Person, f => f.personId, p => p.personId, (f, p) =>
        new // anonymous object
        {
            personId = p.personId,
            addressId = p.addressId,   
            favoriteId = f.favoriteId,
        })
    .AsEnumerable() // database query ends here, the rest is a query in memory
    .Select(x =>
        new PersonDTO
        {
            personId = x.personId,
            addressId = x.addressId,   
            favoriteId = x.favoriteId,
            personProtocol = ppCombined
                .Where(p => p.personId == x.personId)
                .Select(p => new PersonProtocol
                {
                    personProtocolId = p.personProtocolId,
                    activateDt = p.activateDt,
                    personId = p.personId
                })
                .ToList()
        });
Slauma
fonte
10
A parte chave para mim foi acrescentando .AsEnumerable () // fins de consulta de banco de dados aqui, o resto é uma consulta na memória
Sameer Alibhai
2
@ Sluma Então, se eu estou preocupado com o desempenho, devo evitar fazer isso, pois carregaria todos os dados na memória primeiro e depois os consultaria. Devo escrever sql bruto para esses cenários?
Arvand
Parece que @Arvand tem um ótimo argumento. Se você tiver um grande número de registros antes do filtro, isso poderá levar uma enorme mordida nos recursos de memória disponíveis.
Spadelives
5
@ Sluma "Isso não funciona porque o ppCombined é uma coleção de objetos na memória e você não pode associar um conjunto de dados no banco de dados a outro conjunto de dados que está na memória." Onde posso encontrar documentação sobre coisas assim? Estou realmente sem conhecimento dos limites da EF e, quando tento restringir o conjunto de resultados de uma consulta dessa maneira, essa incompetência se torna muito aparente e me atrasa.
Nomenator
1
Boa informação. Estou adicionando essa exceção à minha lista de mensagens de exceção menos intuitivas de todos os tempos. Só faz sentido APÓS você entender por que isso está acontecendo.
DVK
2

Não sei se alguém procura por isso. Eu tive o mesmo problema. Uma seleção na consulta e, em seguida, fazer o where (ou join) e usar a variável select resolveu o problema para mim. (o problema estava na coleção "Reintegraties" para mim)

query.Select(zv => new
            {
                zv,
                rId = zv.this.Reintegraties.FirstOrDefault().Id
            })
            .Where(x => !db.Taken.Any(t => t.HoortBijEntiteitId == x.rId
                                             && t.HoortBijEntiteitType == EntiteitType.Reintegratie
                                             && t.Type == TaakType))
            .Select(x => x.zv);

espero que isso ajude alguém.

Roelant
fonte
6
zv.this.Reintegraties.FirstOrDefault().Idpotencial NullReferenceException
2

No meu caso, consegui resolver o problema da seguinte maneira:

Eu mudei meu código a partir disso:

var r2 = db.Instances.Where(x => x.Player1 == inputViewModel.InstanceList.FirstOrDefault().Player2 && x.Player2 == inputViewModel.InstanceList.FirstOrDefault().Player1).ToList();

Para isso:

var p1 = inputViewModel.InstanceList.FirstOrDefault().Player1;
var p2 = inputViewModel.InstanceList.FirstOrDefault().Player2;
var r1 = db.Instances.Where(x => x.Player1 == p1 && x.Player2 == p2).ToList();
Colin
fonte
Isso não funciona para mim. Como p1e p2estão na memória, sejam eles declarados anonimamente ou por um nome de variável.
Rahat Zaman
2
O tipo de variável não é o problema. No meu caso, o erro foi causado porque estava executando um .FirstOrDefault () dentro da cláusula Where.
Colin
2

Vale a pena adicionar, pois o exemplo de código do OP não fornece contexto suficiente para provar o contrário, mas recebi esse erro também no código a seguir:

public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
    return GetQueryable()
        .FirstOrDefault(x => x.RefersToRetailSaleId.Equals(refersToRetailSaleId));
}

Aparentemente, não posso usar Int32.Equalsneste contexto para comparar um Int32 com um int primitivo; Eu tive que (com segurança) mudar para isso:

public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
    return GetQueryable()
      .FirstOrDefault(x => x.RefersToRetailSaleId == refersToRetailSaleId);
}
James Perih
fonte
A EF aceita Equalsperfeitamente bem.
Gert Arnold
0

Basta adicionar AsEnumerable () eToList (), para que fique assim

db.Favorites
    .Where(x => x.userId == userId)
    .Join(db.Person, x => x.personId, y => y.personId, (x, y).ToList().AsEnumerable()

ToList().AsEnumerable()
khaled saleh
fonte
0

Eu tive esse problema e o que fiz e resolvi foi que usei AsEnumerable()antes da minha cláusula Join. aqui está a minha consulta:

List<AccountViewModel> selectedAccounts;

 using (ctx = SmallContext.GetInstance()) {
                var data = ctx.Transactions.
                    Include(x => x.Source).
                    Include(x => x.Relation).
                    AsEnumerable().
                    Join(selectedAccounts, x => x.Source.Id, y => y.Id, (x, y) => x).
                    GroupBy(x => new { Id = x.Relation.Id, Name = x.Relation.Name }).
                    ToList();
            }

Fiquei me perguntando por que esse problema acontece, e agora acho que é porque, depois que você faz uma consulta via LINQ , o resultado fica na memória e não é carregado nos objetos, não sei qual é esse estado, mas eles estão em alguns estado de transição, eu acho. Então, quando você usa AsEnumerable()ou ToList()etc, os coloca em objetos de memória física e o problema está resolvendo.

ebrahim.mr
fonte