当对象为空时,Linq到对象VS Linq到SQL

时间:2011-11-22 21:49:44

标签: c# .net linq linq-to-sql linq-to-entities

我有这个Linq对象查询:

var result = Users.Where(u => u.Address.Country.Code == 12)

如果地址或国家/地区为空,我会收到例外情况 为什么这个查询不会检查地址是否为空并且只是在该地址之后? 这样我就不需要写这个可怕的查询了:

var result = Users.Where(u => u.Address != null &&
                              u.Address.Country != null &&  
                              u.Address.Country.Code == 12)

在Linq to SQL中,第一个查询将完成工作(当然是出于其他原因)。

是否可以避免linq中的“空检查”对象?

2 个答案:

答案 0 :(得分:6)

不幸的是,在C#(以及许多其他编程语言)中,“null”处理不一致。可空算术解除。也就是说,如果你对可空整数进行算术运算,并且没有操作数为空,那么你得到正常的答案,但是如果它们中的任何一个为空,你就得到null。但“成员访问”操作符解除;如果你给成员访问“。”一个空操作数。运算符,它抛出异常而不是返回空值。

如果我们从头开始设计一个类型系统,我们可能会说所有类型都是可空的,并且包含空操作数的任何表达式都会产生null结果,而不管操作数的类型。因此,使用null接收器或null参数调用方法将产生null结果。这个系统很有意义,但显然我们现在实施它已经太晚了;已经编写了数百万行代码来预期当前的行为。

我们考虑过添加一个“提升”的成员访问运算符,可能标注为.?。所以你可以说where user.?Address.?Country.?Code == 12然后会生成一个可以为空的int,然后可以将其作为可空的int通常比较为12。然而,这一点从来没有超过“是的,在未来的版本中可能很好”的设计过程阶段,所以我不会很快就会想到它。


更新:上面提到的“Elvis”运算符是在C#6.0中实现的。

答案 1 :(得分:5)

不,它是一个空引用异常,就像访问var x = u.Address.Country.Code;将是NullReferenceException一样。

您必须始终确保在LINQ to对象中解析引用的内容不是null,就像使用任何其他代码语句一样。

您可以使用您拥有的&&逻辑执行此操作,也可以链接Where子句(尽管这将包含更多迭代器并且可能执行速度较慢):

var result = Users.Where(u => u.Address != null)
                  .Where(u.Address.Country != null)
                  .Where(u.Address.Country.Code == 12);

注意:以下Maybe()方法仅作为“你也可以”方法提供,我不是说它的好坏,只是展示一些人所做的事情。如果你不喜欢Maybe()我只是重复我见过的各种解决方案,请不要投票......

我已经看过一些Maybe()扩展方法,可以让你做你想做的事情。有些人喜欢/不喜欢这些因为它们是在null引用上运行的扩展方法。我不是说那是好还是坏,只是有些人觉得违反了类似OO的行为。

例如,您可以创建一个扩展方法,如:

public static class ObjectExtensions
{
    // returns default if LHS is null
    public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator)
        where TInput : class
    {
        return (value != null) ? evaluator(value) : default(TResult);
    }

    // returns specified value if LHS is null
    public static TResult Maybe<TInput, TResult>(this TInput value, Func<TInput, TResult> evaluator, TResult failureValue)
        where TInput : class
    {
        return (value != null) ? evaluator(value) : failureValue;
    }
}

然后做:

var result = Users.Where(u => u.Maybe(x => x.Address)
                  .Maybe(x => x.Country)
                  .Maybe(x => x.Code) == 12);

基本上,这只是将null级联到链(或非引用类型的默认值)。

<强>更新

如果您想提供非默认的失败值(如果任何部分为空,则代码为-1),您只需将新的失败值传递给Maybe():

// if you wanted to compare to zero, for example, but didn't want null
// to translate to zero, change the default in the final maybe to -1
var result = Users.Where(u => u.Maybe(x => x.Address)
                  .Maybe(x => x.Country)
                  .Maybe(x => x.Code, -1) == 0);

就像我说的,这只是众多解决方案中的一种。有些人不喜欢能够从null引用类型调用扩展方法,但有些人倾向于使用它来解决这些null级联问题。

但是,目前C#中没有内置的null-de-reference运算符,因此您要么像之前一样使用条件空值检查,请将Where()语句链接起来以便它们过滤掉null,或构建一些内容,让您像上面的null方法一样级联Maybe()