Неможливо створити постійне значення типу У цьому контексті підтримуються лише примітивні типи чи типи перерахування


164

Я отримую цю помилку для запиту нижче

Неможливо створити постійне значення типу API.Models.PersonProtocol. У цьому контексті підтримуються лише примітивні типи чи типи перерахування

ppCombinedвнизу - IEnumerableоб'єкт PersonProtocolType, який побудований за допомогою переліку двох PersonProtocolсписків.

Чому це не вдається? Ми не можемо використовувати LINQ JOINрозділ всередині SELECTз 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
                 })
        });


Відповіді:


232

Це не може працювати, оскільки ppCombinedце сукупність об'єктів у пам'яті, і ви не можете з'єднати набір даних у базі даних з іншим набором даних, що знаходяться в пам'яті. Ви можете спробувати замість того, щоб витягти відфільтровані елементи personProtocolна ppCombinedколекції в пам'яті після того, як ви витягли інші властивості з бази даних:

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

10
Ключовою частиною для мене було додавання .AsEnumerable () // Запит на базу даних закінчується тут, решта - це запит у пам'яті
Sameer Alibhai

2
@Slauma Отже, якщо мене турбує продуктивність, я повинен уникати цього, оскільки він спочатку завантажить усі дані в пам'ять, а потім запитає їх. Чи потрібно писати raw sql для цього сценарію?
Арванд

Здається, @Arvand має чудовий сенс. Якщо перед фільтром у вас є велика кількість записів, це може зайняти величезний перелік наявних ресурсів пам'яті.
spadelives

5
@Slauma "Це не може працювати, тому що ppCombined - це набір об'єктів у пам'яті, і ви не можете об'єднати набір даних у базі даних з іншим набором даних, що знаходяться в пам'яті." Де я можу знайти документацію про такі речі? Мені дійсно не вистачає знань щодо обмежень EF, і коли я намагаюся обмежити такий результат запиту, як такий, ця некомпетентність стає дуже очевидною і сповільнює мене.
Номенатор

1
Хороша інформація. Я додаю цей виняток до мого списку найменш інтуїтивних повідомлень про виключення. Це має сенс лише після того, як ти зрозумієш, чому це відбувається.
DVK

2

Не знаю, чи хтось шукає цього. У мене була така ж проблема. Вибір у запиті, а потім виконання де (або приєднання) та використання змінної select вирішив проблему для мене. (проблема була в колекції "Реінтеграції" для мене)

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

сподіваюся, що це допомагає комусь.


6
zv.this.Reintegraties.FirstOrDefault().Idпотенційний NullReferenceException

2

У моєму випадку мені вдалося вирішити проблему, зробивши наступне:

Я змінив свій код із цього:

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

До цього:

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

Це не працює для мене. Як p1і p2обидва є в пам'яті, незалежно від того, оголошені вони анонімно або ім'ям змінної.
Рахат Заман

2
Тип змінної не є проблемою. У моєму випадку помилка була викликана тим, що робилося .FirstOrDefault () всередині пункту Where.
Колін

2

Варто додати, оскільки зразок коду ОП не містить достатнього контексту, щоб довести інше, але я отримав цю помилку і в наступному коді:

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

Мабуть, я не можу використовувати Int32.Equalsв цьому контексті порівняння Int32 з примітивним int; Мені довелося (безпечно) змінити це:

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

EF сприймає Equalsчудово.
Герт Арнольд

0

Просто додайте AsEnumerable () таToList (), так це виглядає приблизно так

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

ToList().AsEnumerable()

0

У мене виникло це питання, і те, що я зробив і вирішив проблему, було те, що я використовував AsEnumerable()безпосередньо перед моїм пунктом приєднання. ось мій запит:

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

Мені було цікаво, чому ця проблема трапляється, і зараз я думаю, що це тому, що після того, як ви зробите запит через LINQ , результат залишиться в пам'яті, а не завантажиться в об'єкти, я не знаю, що це за стан, але вони є в деяких перехідний стан, я думаю. Потім, коли ви використовуєте AsEnumerable()або ToList()тощо, ви розміщуєте їх у об'єктах фізичної пам'яті, і проблема вирішується.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.