Para pemain ke tipe nilai 'Int32' gagal karena nilai material adalah nol

192

Saya memiliki kode berikut. Saya mendapatkan kesalahan:

"Para pemain ke tipe nilai 'Int32' gagal karena nilai materialized adalah null. Baik parameter generik tipe hasil atau kueri harus menggunakan tipe nullable."

ketika tabel CreditHistory tidak memiliki catatan.

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

Bagaimana saya bisa memodifikasi kueri untuk menerima nilai nol?

zosim
sumber

Jawaban:

328

Kueri linq-to-sql tidak dieksekusi sebagai kode, melainkan diterjemahkan ke dalam SQL. Terkadang ini adalah "abstraksi bocor" yang menghasilkan perilaku tak terduga.

Salah satu kasus tersebut adalah penanganan nol, di mana ada null yang tidak terduga di tempat yang berbeda. ...DefaultIfEmpty(0).Sum(0)dapat membantu dalam kasus ini (sangat sederhana), di mana mungkin tidak ada elemen dan SUMpengembalian sql nullsedangkan c # expect 0.

Pendekatan yang lebih umum adalah menggunakan ??yang akan diterjemahkan ke COALESCEsetiap kali ada risiko bahwa SQL yang dihasilkan menghasilkan nol yang tidak terduga:

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;

Ini pertama kali dilakukan int?untuk memberi tahu kompiler C # bahwa ekspresi ini memang dapat kembali null, meskipun Sum()mengembalikan sebuah int. Kemudian kami menggunakan ??operator normal untuk menangani nullkasing.

Berdasarkan jawaban ini, saya menulis posting blog dengan rincian untuk LINQ ke SQL dan LINQ untuk Entitas.

Anders Abel
sumber
3
terima kasih Anders, solusi dengan DefaultIfEmpty (0) .Sum () berfungsi dengan baik untuk saya. Saya juga sudah mencoba solusi kedua dengan (int?) ... ?? 0 ..., tetapi melempar pengecualian yang sama seperti sebelumnya ..
zosim
Akhirnya sempat menguji dan menyesuaikannya, jadi sekarang versi kedua juga berfungsi.
Anders Abel
1
Jumlah () dan fungsi agregat lainnya akan mengembalikan nol ketika diterapkan ke dataset kosong. Bertentangan dengan definisi mereka, pada kenyataannya mereka mengembalikan versi nullable dari tipe yang mendasarinya.
Suncat2000
2
@recursive: Contoh Anda adalah LINQ-to-Objects, bukan LINQ-to-SQL (atau LINQ-to-Entities). Penyedia data dasar mereka membuat mereka berperilaku berbeda.
Suncat2000
Ini ide yang bagus. Saya memperbarui objek saya untuk memiliki properti yang dapat dibatalkan dan berfungsi sebagai pesona.
Kremena Lalova
8

Untuk mengizinkan Amountbidang yang dapat dibatalkan, cukup gunakan operator penggabungan nol untuk mengonversi nol ke 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();
rekursif
sumber
1
ketika saya menggunakan tip Anda, kompiler mengatakan: Operator '??' tidak dapat diterapkan pada operan tipe 'int' dan 'int'. apakah saya lupa sesuatu?
zosim
@zosim: Itulah alasan untuk menambahkan para pemain ke yang int?pertama.
Anders Abel
saya telah menambahkan int ?, tetapi pengecualian yang sama. Saya akan berterima kasih kepada Anda, ketika Anda akan memiliki dev. untuk memeriksa apa yang salah dalam sintaks ini.
zosim
1
@zosim: Saya tidak mengerti masalahnya. Jika Amountadalah int, maka kami sudah yakin itu tidak bisa nol, dan penggabungan tidak perlu. Jika Anda mendapatkan kesalahan yang Anda katakan, maka Amountitu tidak dapat dibatalkan, itu hanya sebuah int, dalam hal ini mungkin Anda perlu mengubah kolom dbml linq2sql Anda di desainer untuk memungkinkan nol.
Rekursif
1
@recursive: Jumlahnya int, tidak apa-apa. Jumlah sudah bernilai. Saya pikir, bahwa kesalahan di atas terjadi karena tabel CreditHistory kosong. Saya punya satu catatan di tabel pengguna dan 0 catatan di tabel CreditHistory dan kesalahan terjadi. Ketika saya menggunakan DefaultIfEmpty (0) .Sum () berfungsi dengan baik, tetapi dengan ?? 0 itu melempar kesalahan. Pertanyaan saya yang lain adalah apa praktik terbaik dalam kasus ini? DefaultIfEmpty (0)? terima kasih
zosim
4

Anda menggunakan aggregatefungsi yang tidak mendapatkan item untuk melakukan tindakan, Anda harus memverifikasi permintaan linq memberikan beberapa hasil seperti di bawah ini:

var maxOrderLevel =sdv.Any()? sdv.Max(s => s.nOrderLevel):0
Ashwini
sumber
11
Ini akan membuat sdv mengeksekusi dua kali. Yang bukan itu yang Anda inginkan untuk IQueryables
Ody
4

Punya pesan kesalahan ini ketika saya mencoba untuk memilih dari tampilan.

Masalahnya adalah pandangan baru-baru ini telah mendapatkan beberapa baris nol baru (di kolom SubscriberId), dan belum diperbarui di EDMX (basis data EF terlebih dahulu).

Kolom harus tipe Nullable agar bisa berfungsi.

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

Sebelum penyegaran tampilan:

public int SubscriberId { get; set; }

Setelah refresh tampilan:

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

Menghapus dan menambahkan tampilan kembali di EDMX berfungsi.

Semoga ini bisa membantu seseorang.

cinta hidup
sumber
Ini juga masalah saya dan jawaban saya
Simon Nicholls
4

Saya telah menggunakan kode ini dan merespons dengan benar, hanya nilai output yang dapat dibatalkan.

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
                            }
Mohammad Sooori
sumber
1

Saya melihat bahwa pertanyaan ini sudah dijawab. Tetapi jika Anda ingin itu dibagi menjadi dua pernyataan, berikut ini dapat dipertimbangkan.

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;
LCJ
sumber
0

Mendapat kesalahan ini di Entity Framework 6 dengan kode ini saat runtime:

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

Pembaruan dari LeandroSoares:

Gunakan ini untuk eksekusi tunggal:

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

Asli:

Diubah ke ini dan kemudian berhasil:

var fileEventsSum = db.ImportInformations.Any() ? db.ImportInformations.Sum(x => x.FileEvents) : 0;
Ogglas
sumber
1
Bukankah itu akan menjalankannya dua kali?
nawfal
Ini bukan jawaban yang bagus. Ini akan mengambil dari DB dua kali.
Leandro Soares
@nawfal Ini benar tetapi jauh lebih baik daripada kesalahan runtime. Anda benar-benar dapat menggunakan linq-to-sql tetapi dengan lambda lebih sulit. Anda tentu saja dapat menangkap pengecualian tetapi saya pikir solusi lebih buruk dari dua eksekusi.
Ogglas
@LeandroSoares lihat komentar di atas
Ogglas
1
@LeandroSoares Bagus sekali! Saya memperbarui jawaban saya dan menggunakan kode yang Anda berikan dan deskripsi mengapa harus menggunakannya.
Ogglas
0

Saya juga menghadapi masalah yang sama dan diselesaikan dengan membuat kolom sebagai nullable menggunakan "?" operator.

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

Terkadang nol dikembalikan.

pengguna3820036
sumber