Nie można utworzyć stałej wartości typu W tym kontekście obsługiwane są tylko typy pierwotne lub typy wyliczeniowe

164

Otrzymuję ten błąd dla poniższego zapytania

Nie można utworzyć stałej wartości typu API.Models.PersonProtocol. W tym kontekście obsługiwane są tylko typy pierwotne lub typy wyliczeniowe

ppCombinedponiżej znajduje się IEnumerableobiekt programu PersonProtocolType, który jest konstruowany przez konkatację 2 PersonProtocollist.

Dlaczego to się nie udaje? Czy nie możemy używać LINQ JOINklauzula wewnątrz SELECTtematyce 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
źródło

Odpowiedzi:

232

To nie może działać, ponieważ ppCombinedjest to zbiór obiektów w pamięci i nie można połączyć zestawu danych w bazie danych z innym zestawem danych znajdującym się w pamięci. Można spróbować zamiast wydobyć przefiltrowane przedmioty personProtocolz ppCombinedkolekcji w pamięci po pobraniu inne właściwości z bazy danych:

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
źródło
10
Kluczową częścią dla mnie było dodanie .AsEnumerable () // zapytanie do bazy danych kończy się tutaj, reszta to zapytanie w pamięci
Sameer Alibhai
2
@Slauma Więc jeśli martwię się o wydajność, powinienem tego unikać, ponieważ najpierw załadowałoby to wszystkie dane do pamięci, a następnie odpytał. Czy powinienem napisać surowy sql dla tych scenariuszy?
Arvand
Wygląda na to, że @Arvand ma świetną rację. Jeśli masz dużą liczbę rekordów przed filtrem, może to znacznie ograniczyć dostępne zasoby pamięci.
spadelives
5
@Slauma "To nie może działać, ponieważ ppCombined jest zbiorem obiektów w pamięci i nie można połączyć zestawu danych w bazie danych z innym zestawem danych w pamięci." Gdzie mogę znaleźć dokumentację dotyczącą takich rzeczy? Naprawdę brakuje mi wiedzy na temat ograniczeń EF, a kiedy próbuję ograniczyć zestaw wyników zapytania w ten sposób, ta niekompetencja staje się bardzo widoczna i spowalnia mnie.
Nomenator
1
Dobra informacja. Dodaję ten wyjątek do mojej listy najmniej intuicyjnych komunikatów o wyjątkach w historii. Ma to sens tylko wtedy, gdy zrozumiesz, dlaczego tak się dzieje.
DVK
2

Nie wiem, czy ktoś tego szuka. Miałem ten sam problem. Wybranie zapytania, a następnie wykonanie gdzie (lub dołączenie) i użycie zmiennej select rozwiązało problem za mnie. (problem był w mojej kolekcji „Reintegraties”)

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);

mam nadzieję, że to pomoże każdemu.

Roelant
źródło
6
zv.this.Reintegraties.FirstOrDefault().Idpotencjalny NullReferenceException
2

W moim przypadku udało mi się rozwiązać problem, wykonując następujące czynności:

Zmieniłem mój kod z tego:

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

Do tego:

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
źródło
To nie działa dla mnie. As p1i p2oba są w pamięci, niezależnie od tego, czy są zadeklarowane anonimowo, czy przez nazwę zmiennej.
Rahat Zaman
2
Typ zmiennej nie jest problemem. W moim przypadku błąd był spowodowany wykonywaniem funkcji .FirstOrDefault () wewnątrz klauzuli Where.
Colin
2

Warto to dodać, ponieważ przykładowy kod OP nie zapewnia wystarczającego kontekstu, aby udowodnić, że jest inaczej, ale otrzymałem ten błąd również w następującym kodzie:

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

Najwyraźniej nie mogę użyć Int32.Equalsw tym kontekście do porównania Int32 z pierwotną liczbą int; Musiałem (bezpiecznie) zmienić na to:

public RetailSale GetByRefersToRetailSaleId(Int32 refersToRetailSaleId)
{
    return GetQueryable()
      .FirstOrDefault(x => x.RefersToRetailSaleId == refersToRetailSaleId);
}
James Perih
źródło
EF przyjmuje Equalsdoskonale.
Gert Arnold
0

Po prostu dodaj AsEnumerable () iToList (), aby wyglądało to tak

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

ToList().AsEnumerable()
khaled saleh
źródło
0

Miałem ten problem i to, co zrobiłem i rozwiązałem, to ten, którego użyłem AsEnumerable()tuż przed klauzulą ​​Join. oto moje zapytanie:

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();
            }

Zastanawiałem się, dlaczego ten problem się dzieje, a teraz myślę, że to dlatego, że po wykonaniu zapytania przez LINQ wynik będzie w pamięci i nie zostanie załadowany do obiektów, nie wiem, jaki jest ten stan, ale są w niektórych Chyba stan przejściowy . Następnie, używając AsEnumerable()lub ToList(), itp., Umieszczasz je w fizycznych obiektach pamięci i problem jest rozwiązany.

ebrahim.mr
źródło