การแปลงค่าเป็นประเภท '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();

ฉันจะแก้ไขแบบสอบถามเพื่อยอมรับค่า Null ได้อย่างไร

คำตอบ:


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
ขอบคุณ Anders การแก้ปัญหาด้วย DefaultIfEmpty (0) .Sum () ใช้งานได้ดีสำหรับฉัน ฉันได้ลองวิธีที่สองด้วย (int?) ... ?? 0 ... แต่มันมีข้อผิดพลาดเหมือนเดิม ..
zosim

ในที่สุดก็มีการทดสอบและปรับมันแล้วตอนนี้รุ่นที่สองก็ใช้ได้เช่นกัน
Anders Abel

1
ผลรวม () และฟังก์ชั่นการรวมอื่น ๆ จะส่งคืน null เมื่อนำไปใช้กับชุดข้อมูลที่ว่างเปล่า ตรงกันข้ามกับคำจำกัดความของพวกเขาในความเป็นจริงพวกเขากลับมาเป็นรุ่นพื้นฐานประเภท nullable
Suncat2000

2
@recursive: ตัวอย่างของคุณคือ LINQ-to-Objects ไม่ใช่ LINQ-to-SQL (หรือ LINQ-to-Entities) ผู้ให้บริการข้อมูลพื้นฐานของพวกเขาทำให้พวกเขาทำงานแตกต่างกัน
Suncat2000

นี่เป็นความคิดที่ดี ฉันอัปเดตออบเจกต์ส่งคืนของฉันเพื่อให้มีคุณสมบัติที่ไม่สามารถใช้งานได้และใช้เป็นเครื่องราง
Kremena Lalova

8

หากต้องการอนุญาตให้Amountฟิลด์ที่มีค่าว่างให้ใช้ตัวดำเนินการรวมค่า null เพื่อแปลงค่า Null เป็น 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
เมื่อฉันใช้เคล็ดลับคอมไพเลอร์พูดว่า: Operator '??' ไม่สามารถใช้กับตัวถูกดำเนินการประเภท 'int' และ 'int' ฉันจะลืมบางสิ่งได้อย่างไร
zosim

@zosim: นั่นคือเหตุผลที่เพิ่มนักแสดงint?ก่อน
Anders Abel

ฉันได้เพิ่ม int แล้ว แต่มีข้อยกเว้นเดียวกัน ฉันจะขอบคุณคุณเมื่อคุณจะมี dev env เพื่อตรวจสอบสิ่งผิดปกติในไวยากรณ์นี้
zosim

1
@zosim: ฉันไม่เข้าใจปัญหา ถ้าAmountเป็นintแล้วเรามั่นใจแล้วว่าไม่สามารถเป็นโมฆะและการรวมตัวกันนั้นไม่จำเป็น หากคุณได้รับข้อผิดพลาดที่คุณพูดนั้นAmountจะไม่เป็นโมฆะมันเป็นเพียงแค่intในกรณีนี้คุณอาจจำเป็นต้องเปลี่ยนคอลัมน์ linq2sql dbml ในตัวออกแบบเพื่ออนุญาต null
เรียกซ้ำ

1
@recursive: จำนวนเงินเป็น int ไม่เป็นไร จำนวนเงินมีค่าอยู่แล้ว ฉันคิดว่าข้อผิดพลาดดังกล่าวเกิดขึ้นเนื่องจากตาราง CreditHistory ว่างเปล่า ฉันมีหนึ่งระเบียนในตารางผู้ใช้และ 0 บันทึกในตารางเครดิตและประวัติข้อผิดพลาดเกิดขึ้น เมื่อฉันใช้ DefaultIfEmpty (0) .Sum () มันใช้งานได้ดี แต่ใช้ ?? 0 มันพ่นข้อผิดพลาด คำถามอื่นของฉันคือสิ่งที่ปฏิบัติที่ดีที่สุดในกรณีนี้คืออะไร? DefaultIfEmpty (0)? ขอบคุณ
zosim

4

คุณกำลังใช้aggregateฟังก์ชั่นที่ไม่ได้รับไอเท็มที่จะดำเนินการคุณต้องตรวจสอบว่าคิวรี linq ให้ผลลัพธ์ดังนี้:

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

11
นี่จะทำให้ sdv ทำงานสองครั้ง ซึ่งไม่ใช่สิ่งที่คุณต้องการสำหรับ IQueryables
Ody

4

มีข้อความแสดงข้อผิดพลาดนี้เมื่อฉันพยายามเลือกจากมุมมอง

ปัญหาคือมุมมองที่เพิ่งได้รับแถว null ใหม่บางส่วน (ในคอลัมน์ SubscriberId) และไม่ได้รับการปรับปรุงใน EDMX (ฐานข้อมูล EF ก่อน)

คอลัมน์ต้องเป็นประเภทที่ใช้งานได้เพื่อให้มันทำงานได้

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

ก่อนดูการรีเฟรช:

public int SubscriberId { get; set; }

หลังจากดูการรีเฟรช:

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

การลบและเพิ่มมุมมองกลับใน EDMX ใช้งานได้

หวังว่าจะช่วยใครซักคน


นี่เป็นปัญหาของฉันและคำตอบของฉัน
Simon Nicholls

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

นี่ไม่ใช่คำตอบที่ดี มันจะดึงข้อมูลจาก DB สองครั้ง
ลีอันโดร Soares

@nawfal สิ่งนี้เป็นจริง แต่ดีกว่าข้อผิดพลาดรันไทม์ คุณสามารถใช้ linq-to-sql ได้ แต่ด้วยแลมบ์ดามันยากกว่า แน่นอนคุณสามารถจับข้อยกเว้น แต่ฉันคิดว่าทางออกนั้นแย่กว่าการประหารสองครั้ง
Ogglas

@LeandroSoares ดูความคิดเห็นด้านบน
Ogglas

1
@LeandroSoares คนดี! ฉันอัปเดตคำตอบของฉันและใช้รหัสที่คุณให้ไว้และคำอธิบายว่าทำไมจึงต้องใช้แทน
Ogglas

0

ฉันยังประสบปัญหาเดียวกันและแก้ไขด้วยการทำให้คอลัมน์เป็นโมฆะโดยใช้ "?" ผู้ประกอบการ

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

บางครั้งกลับเป็นโมฆะ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.