如何在LINQ子查询中处理Null值?

5
我有一个子查询,返回子表中最近的值。在某些情况下,子查询没有返回值。下面的查询在运行时失败,因为MemberPrice的推断类型是decimal类型,且不可为空。
简化后的查询:
Dim q = From s In dc.STOCKs _
        Select s.ID, MemberPrice = _
          (From mp In dc.STOCKPRICEs Where mp.NUMBER = s.NUMBER _
          Order By dc.date Descending _
          Select mp.PRICE).FirstOrDefault

在SQL中,子查询会包含Top (1),当为空时返回Null。我该如何处理LINQ中的这个问题?是否有一种方法使MemberPrice可空,或者如果找不到则将值默认为零(或更优雅的解决方案)?
非常感谢,斯图尔特。

你能定义一下你所说的“失败”是什么意思吗?如果子查询为空,FirstOrDefault应该返回Nothing,因此在这种情况下MemberPrice应该为0。 - JaredPar
我遇到了一个System.InvalidOperationException {"不能将空值分配给类型为System.Decimal的成员,该成员是非可空值类型。"}。同时,如果是 null 的话,我本来期望它返回 0,所以我必须漏掉了什么东西。 - Stuart
mp.PRICE 的类型是什么? - JaredPar
@Stuar,你解决了吗?因为我也有同样的问题。 - Vikas
@Vikas,是的,下面Robert的答案就是解决方案。将类型转换为可空类型可以解决我一直遇到的一系列问题。 - Stuart
进一步澄清,FirstOrDefault 将结果从 Decimal 序列转换为单个的 Decimal?。这就是 null 的来源。 - Suncat2000
4个回答

8

Stuart,

我在数据库中将价格字段更改为不允许为空,并且出现了与你相同的错误:

"Operator '??' cannot be applied to operands of type 'decimal' and 'int'". 

正如您所指出的那样,在数据库中将Price设置为不允许为空时,空合并运算符不再起作用,因为它期望看到可空decimal类型:

decimal?

如果我去掉空值合并运算符并运行不包含价格的测试用例,会得到以下结果:
"The null value cannot be assigned to a member with type System.Decimal which is a non-nullable value type.."

这是可行的代码。在应用空合并运算符之前,我将子查询结果强制转换为十进制数。

public class Class1
{

    DataClasses1DataContext dc = new DataClasses1DataContext();

    public decimal test(int stockID)
    {

        var q = from s in dc.Stocks
                where s.StockID == stockID
                select new
                {
                    StockID = s.StockID,
                    memberPrice = ((decimal?)(from mp in dc.StockPrices
                                   where mp.StockID == s.StockID
                                   select mp.Price).FirstOrDefault()) ?? 0
                };

        return q.FirstOrDefault().memberPrice;
    }
}

Robert,非常感谢您不遗余力地解决这个问题。 - Stuart

3

Stuart,请试一下这个:

Dim q = From s In dc.STOCKs _
    Select s.ID, MemberPrice = _
      if((From mp In dc.STOCKPRICEs Where mp.NUMBER = s.NUMBER _
      Order By dc.date Descending _
      Select mp.PRICE).FirstOrDefault),0)

空值合并运算符将会把MemberPrice的null值强制转换为0。

1
vb.net 中不存在空合并运算符 ??。 - user434917
1
是的,它可以:If函数。就像If(possibleNullValue, valueIfNull)。 - Paulo Santos
@Robert,我编辑了代码示例,使用了VB.Net版本的合并运算符。 - JaredPar
我得到了以下编译器错误:“二进制'If'表达式中的第一个操作数必须是可空类型或引用类型。” - Stuart
我得到了相同的编译器错误,因为MemberPrice被推断为值类型(decimal)。 - Stuart

0

斯图尔特,

这是我在我的电脑上让它工作的方法。很抱歉它是用c#编写的;自从我使用VB以来已经过了太长时间。

请注意在“select”语句中使用“new”运算符以及在FirstOrDefault()之后使用null合并运算符的用法。

public class Class1
{

    DataClasses1DataContext dc = new DataClasses1DataContext();

    public decimal MemberPrice(int stockID)
    {

        var q = from s in dc.Stocks
                where s.StockID == stockID
                select new
                {
                    StockID = s.StockID,
                    memberPrice = (from mp in dc.StockPrices
                                   where mp.StockID == s.StockID
                                   select mp.Price).FirstOrDefault() ?? 0
                };

        return q.FirstOrDefault().memberPrice;
    }
}

我在C#中使用此代码时遇到了相同的编译器错误,因为memberPrice被推断为decimal(而不是Nullable<decimal>)。错误是“运算符'??'不能应用于类型为'decimal'和'int'的操作数”。您的机器上Price的类型是什么? - Stuart
我的机器上的价格类型是Decimal(18,0),允许为空。 - Robert Harvey

0

DefaultIfEmpty扩展方法是否符合您的需求?


我已经使用它进行了平面外连接,但无法解决如何避免将空值分配给mp.Price十进制值类型的问题。您有任何语法建议吗? - Stuart

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