对Linq查询进行null检查是否惯用

本文关键字:检查 是否 null Linq 查询 | 更新日期: 2023-09-27 18:00:47

使用Linq到SQL,我想知道以下三个中哪一个更惯用

  • foos.Where(foo => foo.Bar.HasValue && foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar.Value < 42)
  • foos.Where(foo => foo.Bar < 42)

第一个选项生成一个额外的Bar IS NOT NULL谓词,该谓词可能在大多数DBMS中被优化掉了。如果查询对象而不是数据库,则null检查将是强制性的,但由于可以创建可能在对象上失败但在数据库上失败的通用IQueriable<Foo>查询,因此第一个选项始终有效,尽管Linq和SQL代码都比第二个选项长一点。Michael Liu提供的第三个选项似乎是两全其美的,但在foo.Bar具有bool类型的情况下不起作用foos.Where(foo => foo.Bar)(由于此处未进行隐式转换而导致类型错误)。

如果在最初设计的上下文之外使用,是否应该努力编写不会失败的通用查询?

对Linq查询进行null检查是否惯用

底层数据存储技术多年来一直在发生变化。代码的寿命似乎比任何人想象的都要长。我不会建立关于底层存储提供商的假设。

如果Linq是您的抽象层,那么假设任何Linq提供程序都可能在应用程序生命周期的某个时刻插入。

前者生成了一个额外的Bar-ISNOTNULL谓词,该谓词可能在大多数DBMS中被优化掉了。

我不是数据库性能专家,但我认为与许多查询的总体成本相比,额外的ISNOT NULL的性能成本会很小。

第三个选项是使用"提升"的<运算符,如果任一操作数为null,则返回false。这使您可以省略对null的显式检查,并适用于所有LINQ提供程序:

foos.Where(foo => foo.Bar < 42)

如果foos的编译时类型是IEnumerable<Foo>,则foo.Bar < 42等效于

foo.Bar.GetValueOrDefault() < 42 && foo.Bar.HasValue

除了CCD_ 12仅被访问一次之外。(我是通过在Reflector中反编译程序得到的。有趣的是,编译器选择在检查null之前执行比较,但这作为一种微观优化是有意义的。)

如果foos的编译时类型是IQueryable<Foo>,则foo.Bar < 42等效于

foo.Bar < (int?)42

是否以及如何检查空值取决于LINQ提供者。


更新:如果foo.Bar的类型是bool?,并且您只想包括foo.Bar为true(即既不是false也不是null)的记录,那么您可以编写

foos.Where(foo => foo.Bar == true)

foos.Where(foo => foo.Bar.GetValueOrDefault())

我会编写特定于您正在使用的提供程序的查询。Linq-to-objects可能看起来与linq-to-sql/entities相似,但它们的行为非常不同。

  1. 它们不支持相同的功能
  2. 它们不返回相同的类型
  3. 它们的执行方式不同

如果您的linq提供程序将来发生更改,那么无论如何,您很可能不得不重写查询。例如,有一些特定于提供程序的类,如SqlFunctions,它们只适用于给定的提供程序。你无法预测未来的提供商将支持什么,也无法预测它将如何工作。

linq中对实体的简单分组查询

var query = from f in foos
            group f by new { f.Name, f.Bar.Value } into g
            select ...

将导致一个大不相同的linq-to-objects查询。尝试找到一个同时满足两者的查询是否有益?

Linq到SQL的转换器将负责输出正确的SQL。在这种情况下,伪ORM代码反映了可以为null的Bar列。当处理返回的结果时,这在应用程序代码中很有用,但对于MSSQL查询的where子句来说就不那么有用了。我会让你第二次选择。