Не вдалося передати значення типу Int32 ', оскільки матеріалізоване значення є нульовим


193

У мене є такий код. Я отримую помилку:

"Тип передачі значення" Int32 "не вдався, оскільки матеріалізоване значення є нульовим. Або загальний параметр типу результату, або запит повинен використовувати нульовий тип."

коли таблиця CreditHistory не має записів.

var creditsSum = (from u in context.User
                  join ch in context.CreditHistory on u.ID equals ch.UserID                                        
                  where u.ID == userID
                  select ch.Amount).Sum();

Як я можу змінити запит, щоб прийняти нульові значення?

Відповіді:


330

Запит linq-to-sql не виконується як код, а переводиться в SQL. Іноді це "нещільна абстракція", яка спричиняє несподівану поведінку.

Один з таких випадків - обробка нуля, де в різних місцях можуть бути несподівані нулі. ...DefaultIfEmpty(0).Sum(0)може допомогти в цьому (досить простому) випадку, коли може не бути елементів і SUMвіддачі sql, nullтоді як c # очікує 0.

Більш загальним підходом є використання, ??яке буде переведено на те, COALESCEколи є ризик, що згенерований SQL поверне несподіваний нуль:

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select (int?)ch.Amount).Sum() ?? 0;

Це спочатку означає, щоб int?сказати компілятору C #, що цей вираз дійсно може повернутися null, навіть незважаючи на те, що Sum()повертає int. Тоді ми використовуємо звичайний ??оператор для обробки nullсправи.

На основі цієї відповіді я написав допис у блозі з деталями як для LINQ до SQL, так і для LINQ для сутностей.


3
спасибі Андерс, рішення з DefaultIfEmpty (0) .Sum () добре працює для мене. Я також спробував друге рішення з (int?) ... ?? 0 ..., але це кидає той самий виняток, що і раніше ..
zosim

Нарешті обійшлося протестувати це і відкоригувати його, тому тепер працює і друга версія.
Андерс Абель

1
Сума () та інші сукупні функції повернуть нуль при застосуванні до порожнього набору даних. Всупереч їх визначенню, насправді вони повертають нульову версію базового типу.
Suncat2000

2
@recursive: Ваш приклад - LINQ-to-Objects, а не LINQ-to-SQL (або LINQ-to-Entities). Їх основні постачальники даних змушують їх вести себе по-різному.
Suncat2000

Це була гарна ідея. Я оновив мій об'єкт повернення, щоб він міняв властивості, і це працювало як шарм.
Кремена Лалова

8

Щоб дозволити Amountнульове поле, просто скористайтеся оператором злиття нуля, щоб перетворити нулі в 0.

var creditsSum = (from u in context.User
              join ch in context.CreditHistory on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch.Amount ?? 0).Sum();

1
коли я використовую вашу пораду, компілятор каже: Оператор "??" не можна застосовувати до операндів типу 'int' та 'int'. я щось забув?
zosim

@zosim: Це причина, щоб додати акторський склад int?спочатку.
Андерс Абель

я додав Int ?, але той самий виняток. Я буду вам вдячний, коли у вас буде dev env. щоб перевірити, що в цьому синтаксисі не так.
zosim

1
@zosim: Я не розумію проблеми. Якщо Amountє int, то ми вже впевнені, що це не може бути недійсним, і з'єднання є зайвим. Якщо ви отримуєте помилку, про яку ви сказали, то Amountце не зводиться до нуля, це просто intанкета, і, можливо, вам потрібно змінити свій колонку dbml linq2sql в дизайнері, щоб дозволити нулі.
рекурсивна

1
@recursive: Сума є int, це нормально. Сума вже має значення. Я думаю, що помилка вище трапляється через те, що таблиця CreditHistory порожня. У мене є один запис у таблиці користувача та 0 записів у таблиці CreditHistory, і сталася помилка. Коли я використовую DefaultIfEmpty (0) .Sum (), він працює добре, але з ?? 0 він кидає помилку. Моє ще одне питання - яка найкраща практика в даному випадку? DefaultIfEmpty (0)? дякую
zosim

4

Ви використовуєте aggregateфункцію, яка не отримує елементи для виконання дій, ви повинні переконатися, що запит linq дає певний результат, як показано нижче:

var maxOrderLevel =sdv.Any()? sdv.Max(s => s.nOrderLevel):0

11
Це зробило б виконання SDD двічі. Що ви не хочете для IQueryables
Ody

4

Було це повідомлення про помилку, коли я намагався вибрати з перегляду.

Проблема полягала в тому, що нещодавно перегляд отримав нові нульові рядки (у стовпці SubscriberId), і він не був оновлений у EDMX (спочатку база даних EF).

Стовпець мав бути типу Nullable, щоб він працював.

var dealer = Context.Dealers.Where (x => x.dealerCode == dealerCode) .irstOrDefault ();

Перед оновленням перегляду:

public int SubscriberId { get; set; }

Після оновлення перегляду:

public Nullable<int> SubscriberId { get; set; }

Видалення та додавання перегляду ще в EDMX спрацювало.

Сподіваюся, це комусь допоможе.


Це було також моїм питанням і моєю відповіддю
Саймон Ніколлс

4

Я використав цей код, і він відповідає правильно, лише вихідне значення є нульовим.

var packesCount = await botContext.Sales.Where(s => s.CustomerId == cust.CustomerId && s.Validated)
                                .SumAsync(s => (int?)s.PackesCount);
                            if(packesCount != null)
                            {
                                // your code
                            }
                            else
                            {
                                // your code
                            }

1

Я бачу, що на це питання вже відповіли. Але якщо ви хочете, щоб він розділився на два твердження, може бути розглянуто наступне.

var credits = from u in context.User
              join ch in context.CreditHistory 
                  on u.ID equals ch.UserID                                        
              where u.ID == userID
              select ch;

var creditSum= credits.Sum(x => (int?)x.Amount) ?? 0;

0

Отримала цю помилку в Entity Framework 6 з цим кодом під час виконання:

var fileEventsSum = db.ImportInformations.Sum(x => x.FileEvents)

Оновлення від LeandroSoares:

Використовуйте це для одного виконання:

var fileEventsSum = db.ImportInformations.Sum(x => (int?)x.FileEvents) ?? 0

Оригінал:

Змінили це, а потім спрацювало:

var fileEventsSum = db.ImportInformations.Any() ? db.ImportInformations.Sum(x => x.FileEvents) : 0;

1
Хіба що не виконати це двічі?
nawfal

Це не гарна відповідь. Він буде завантажений з БД двічі.
Леандро Соарес

@nawfal Це правда, але це набагато краще, ніж помилка виконання. Ви можете абсолютно використовувати linq-to-sql, але з лямбда - це складніше. Ви, звичайно, можете зловити виняток, але я думаю, що рішення є гіршим, ніж два страти.
Огглас

@LeandroSoares дивіться вище коментар
Огглас

1
@LeandroSoares Гарний! Я оновив свою відповідь і використав наданий вами код та описав, навіщо її використовувати.
Огглас

0

Я також зіткнувся з тією ж проблемою і вирішив, зробивши стовпчик як нульовим, використовуючи "?" оператор.

Sequnce = db.mstquestionbanks.Where(x => x.IsDeleted == false && x.OrignalFormID == OriginalFormIDint).Select(x=><b>(int?)x.Sequence</b>).Max().ToString();

Іноді нульове значення повертається.

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