由于实例化的值为空,因此将值类型“Int32”转换失败。

210

我有以下代码。出现错误:

"转换为值类型 'Int32' 失败,因为实体的值为 null。要么结果类型的泛型参数或查询必须使用可空类型。"

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

我该如何修改查询以接受空值?

8个回答

361
LINQ to SQL查询不是以代码形式执行,而是被转换为SQL。有时,这种“泄漏的抽象”会产生意外的行为。
其中一个例子是空值处理,在不同的地方可能会有意外的空值。在这种(非常简单的)情况下,...DefaultIfEmpty(0).Sum(0) 可以帮助处理,当 SQL 的 SUM 返回 null 而 C# 期望是 0 时。
更一般的方法是使用 ??,每当存在生成的 SQL 返回意外的 null 值的风险时,它都将被转换为 COALESCE
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到Entities的详细内容。


3
谢谢Anders,使用DefaultIfEmpty(0).Sum()的解决方案对我很有效。我还尝试了第二个解决方案(int?) ... ?? 0...,但它抛出了与之前相同的异常。 - zosim
终于有时间测试并调整了一下,现在第二个版本也可以工作了。 - Anders Abel
1
当应用于空数据集时,Sum()和其他聚合函数将返回null。与它们的定义相反,在实际情况中,它们返回基础类型的可空版本。 - 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
当我使用您的提示时,编译器会显示:运算符“??”不能应用于类型为“int”和“int”的操作数。我忘了什么吗? - zosim
@zosim:这就是先将 int? 强制转换的原因。 - Anders Abel
我已经添加了int?,但是仍然出现相同的异常。当您有开发环境时,请检查此语法中的错误,我将不胜感激。 - zosim
1
@zosim:我不理解这个问题。如果“Amount”是一个“int”,那么我们已经确定它不能为null,合并运算是不必要的。如果您遇到了所说的错误,则“Amount”不可为空,它只是一个“int”,在这种情况下,也许您需要在设计器中更改您的linq2sql dbml列以允许空值。 - recursive
1
@recursive:Amount是int类型,没问题。Amount已经有值了。我认为上面的错误是因为CreditHistory表为空。我在User表中有一条记录,在CreditHistory表中没有记录,就出现了错误。当我使用DefaultIfEmpty(0).Sum()时,它可以正常工作,但是用?? 0会抛出错误。我的另一个问题是,在这种情况下最好的做法是什么?是DefaultIfEmpty(0)吗?谢谢。 - zosim
@zosim:是的,这正是DefaultIfEmpty的作用。 - recursive

6

当我试图从一个视图中进行选择时,出现了这个错误信息。

问题在于该视图最近新增了一些空行(在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

6

我已经使用了这段代码,它的响应正确,只是输出值可为空。

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
                            }

谢谢!!!哦天啊,那真是棘手,所以如果你要连接结果,它必须在最终位置。 - Seabizkit

5

您正在使用aggregate函数,但是没有获取项目以执行操作,您必须验证linq查询是否按照以下方式给出结果:

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

11
这会让 sdv 执行两次,这并不是您对于 IQueryables 希望的结果。 - Ody

3

我看到这个问题已经有了答案。但如果你想将其分成两个语句,则可以考虑以下内容。

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
这不是一个好的答案。它会从数据库中检索两次。 - Leandro Soares
@nawfal 这是真的,但它比运行时错误要好得多。你绝对可以使用linq-to-sql,但使用lambda会更难。当然,你可以捕获异常,但我认为这个解决方案比两次执行更糟糕。 - Ogglas
@LeandroSoares 请参见上面的评论。 - Ogglas
@Ogglas,从性能方面考虑,最好这样做:db.ImportInformations.Sum(x => (int?)x.FileEvents) ?? 0 - Leandro Soares
1
@LeandroSoares 不错!我更新了我的答案,并使用了你提供的代码以及为什么要使用它的描述。 - Ogglas

0

我也曾面临同样的问题,并通过使用“?”运算符将列设置为可空来解决它。

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

有时会返回 null。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接