Việc chuyển sang loại giá trị 'Int32' không thành công vì giá trị cụ thể hóa là null


192

Tôi có mã sau đây. Tôi đang gặp lỗi:

"Việc chuyển sang loại giá trị 'Int32' không thành công vì giá trị cụ thể hóa là null. Tham số chung của loại kết quả hoặc truy vấn phải sử dụng loại không thể."

khi bảng CreditHistory không có hồ sơ.

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

Làm cách nào để sửa đổi truy vấn để chấp nhận giá trị null?

Câu trả lời:


328

Một truy vấn linq-to-sql không được thực thi dưới dạng mã, mà được dịch sang SQL. Đôi khi đây là một "sự trừu tượng rò rỉ" mang lại hành vi bất ngờ.

Một trường hợp như vậy là xử lý null, trong đó có thể có null bất ngờ ở những nơi khác nhau. ...DefaultIfEmpty(0).Sum(0)có thể giúp trong trường hợp (khá đơn giản) này, trong đó có thể không có phần tử và SUMlợi nhuận của sql nulltrong khi c # mong đợi 0.

Một cách tiếp cận tổng quát hơn là sử dụng ??nó sẽ được dịch sang COALESCEbất cứ khi nào có nguy cơ SQL được tạo sẽ trả về một giá trị null bất ngờ:

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;

Điều này đầu tiên int?để nói với trình biên dịch C # rằng biểu thức này thực sự có thể trả về null, mặc dù Sum()trả về một int. Sau đó, chúng tôi sử dụng các ??toán tử bình thường để xử lý nulltrường hợp.

Dựa trên câu trả lời này, tôi đã viết một bài đăng trên blog với các chi tiết cho cả LINQ to SQL và LINQ to Entities.


3
cảm ơn Anders, giải pháp với Default IfEmpty (0) .Sum () hoạt động tốt với tôi. Tôi cũng đã thử giải pháp thứ hai với (int?) ... ?? 0 ..., nhưng nó ném ngoại lệ giống như trước đây ..
zosim

Cuối cùng cũng có xung quanh để kiểm tra điều này và điều chỉnh nó, vì vậy bây giờ phiên bản thứ hai cũng hoạt động.
Anders Abel

1
Sum () và các hàm tổng hợp khác sẽ trả về null khi áp dụng cho tập dữ liệu trống. Trái với định nghĩa của họ, trong thực tế, họ trả về một phiên bản nullable của loại cơ bản.
Suncat2000

2
@recursive: Ví dụ của bạn là LINQ-to-Object, không phải LINQ-to-SQL (hoặc LINQ-to-Entities). Các nhà cung cấp dữ liệu cơ bản của họ làm cho họ cư xử khác nhau.
Suncat2000

Đây là một ý tưởng tốt. Tôi đã cập nhật đối tượng trả về của mình để có các thuộc tính nullable và nó hoạt động như một nét quyến rũ.
Kremena Lalova

8

Để cho phép Amounttrường nullable , chỉ cần sử dụng toán tử hợp nhất null để chuyển null thành 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
Khi tôi sử dụng mẹo của bạn, trình biên dịch sẽ nói: Toán tử '??' không thể được áp dụng cho toán hạng loại 'int' và 'int'. tôi quên một cái gì đó?
zosim

@zosim: Đó là lý do để thêm diễn viên vào int?đầu tiên.
Anders Abel

tôi đã thêm int?, nhưng ngoại lệ tương tự. Tôi sẽ biết ơn bạn, khi bạn sẽ có dev env. để kiểm tra những gì sai trong cú pháp này.
zosim

1
@zosim: Tôi không hiểu vấn đề. Nếu Amountlà một int, thì chúng tôi đã chắc chắn rằng nó không thể là null và việc kết hợp lại là không cần thiết. Nếu bạn nhận được lỗi mà bạn đã nói, thì đó Amountkhông phải là intnull , đó chỉ là một trường hợp có lẽ bạn cần thay đổi cột dbml linq2sql trong trình thiết kế để cho phép null.
đệ quy

1
@recursive: Số tiền là int, không sao cả. Số tiền đã có giá trị. Tôi nghĩ rằng lỗi xảy ra ở trên là do bảng CreditHistory trống. Tôi có một bản ghi trong bảng Người dùng và 0 bản ghi trong bảng CreditHistory và đã xảy ra lỗi. Khi tôi sử dụng Default IfEmpty (0) .Sum () nó hoạt động tốt, nhưng với ?? 0 nó ném lỗi. Một câu hỏi khác của tôi là thực hành tốt nhất trong trường hợp này là gì? Mặc định IfEmpty (0)? cảm ơn
zosim

4

Bạn đang sử dụng aggregatechức năng không nhận các mục để thực hiện hành động, bạn phải xác minh truy vấn linq sẽ cho kết quả như sau:

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

11
Điều này sẽ làm cho sdv thực hiện hai lần. Đó không phải là những gì bạn muốn cho IQueryables
Ody

4

Có thông báo lỗi này khi tôi đang cố gắng chọn từ một khung nhìn.

Vấn đề là chế độ xem gần đây đã thu được một số hàng null mới (trong cột SubscackerId) và nó đã không được cập nhật trong EDMX (cơ sở dữ liệu EF trước tiên).

Cột phải là loại Nullable để nó hoạt động.

var Deal = Context.Dealers.Where (x => x.dealerCode == DealCode) .Đầu tiên Default ();

Trước khi xem làm mới:

public int SubscriberId { get; set; }

Sau khi xem làm mới:

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

Xóa và thêm chế độ xem lại trong EDMX đã hoạt động.

Hy vọng nó sẽ giúp được ai đó.


Đây cũng là vấn đề của tôi và câu trả lời của tôi
Simon Nicholls

4

Tôi đã sử dụng mã này và nó phản hồi chính xác, chỉ có giá trị đầu ra là không thể.

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

Tôi thấy rằng câu hỏi này đã được trả lời. Nhưng nếu bạn muốn chia nó thành hai tuyên bố, có thể xem xét sau đây.

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

Đã gặp lỗi này trong Entity Framework 6 với mã này khi chạy:

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

Cập nhật từ LeandroSoares:

Sử dụng điều này để thực hiện duy nhất:

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

Nguyên:

Thay đổi điều này và sau đó nó hoạt động:

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

1
Điều đó sẽ không thực hiện nó hai lần?
nawfal

Đây không phải là một câu trả lời tốt. Nó sẽ lấy từ DB hai lần.
Leandro Soares

@nawfal Điều này đúng nhưng tốt hơn rất nhiều so với lỗi thời gian chạy. Bạn hoàn toàn có thể sử dụng linq-to-sql nhưng với lambda thì khó hơn. Tất nhiên bạn có thể bắt ngoại lệ nhưng tôi nghĩ rằng giải pháp đó còn tệ hơn hai lần thực thi.
Ogglas

@LeandroSoares xem bình luận ở trên
Ogglas

1
@LeandroSoares Đẹp một! Tôi đã cập nhật câu trả lời của mình và sử dụng mã bạn cung cấp và mô tả lý do để sử dụng nó thay thế.
Ogglas

0

Tôi cũng đã phải đối mặt với cùng một vấn đề và giải quyết thông qua việc tạo cột thành nullable bằng cách sử dụng "?" nhà điều hành.

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

Đôi khi null được trả lại.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.