查询中的可为 Null 运算符

可为 Null 运算符是二进制算术或比较运算符,它们适用于一端或两端的可为 null 的算术类型。 使用数据源(例如允许 null 代替实际值的数据库)时,会出现可以为 null 的类型。 查询表达式中使用可为 Null 的运算符。 除了算术和比较的可为 null 运算符外,转换运算符还可用于在可以为 null 的类型之间进行转换。 某些查询运算符也有可为 null 的版本。

注释

可以为 null 的运算符通常用于 查询表达式。 如果不使用查询表达式,则无需知道或使用这些运算符。

可为 Null 运算符的表

下表列出了 F# 中支持的可为 null 运算符。

左侧可为 Null 右侧可为 Null 双方可为 null
?>= >=? ?>=?
?> >? ?>?
?<= <=? ?<=?
?< <? ?<?
?= =? ?=?
?<> <>? ?<>?
?+ +? ?+?
?- -? ?-?
?* *? ?*?
?/ /? ?/?
?% %? ?%?

注解

可以为 null 的运算符包含在命名空间 FSharp.LinqNullableOperators 模块中。 可为 null 数据的类型为 System.Nullable<'T>

在查询表达式中,从允许 null 而不是值的数据选择数据源中的数据时,会出现可以为 null 的类型。 在 SQL Server 数据库中,表中的每个数据列都有一个属性,指示是否允许 null。 如果允许 null,则从数据库返回的数据可以包含不能由基元数据类型(例如 intfloat等)表示的 null 值。 因此,数据将作为替代int返回System.Nullable<int>System.Nullable<float>而不是float返回。 实际值可以通过使用属性从System.Nullable<'T>对象中获取,并且可以通过调用HasValue方法来确定对象是否System.Nullable<'T>具有Value值。 另一个有用的方法是该方法 System.Nullable<'T>.GetValueOrDefault ,该方法允许获取相应类型的值或默认值。 默认值是某种形式的“零”值,例如 0、0.0 或 false

可以使用通常的转换运算符(如 intfloat)将可以为 null 的类型转换为不可为 null 的基元类型。 也可以使用可为 null 类型的转换运算符从一个可以为 null 的类型转换为另一个可以为 null 的类型。 适当的转换运算符与标准运算符同名,但它们位于单独的模块中,即 FSharp.Linq 命名空间中的可为 Null 模块。 通常,在使用查询表达式时打开此命名空间。 在这种情况下,可以通过将前缀 Nullable. 添加到适当的转换运算符来使用可为 null 的转换运算符,如以下代码所示。

open Microsoft.FSharp.Linq

let nullableInt = new System.Nullable<int>(10)

// Use the Nullable.float conversion operator to convert from one nullable type to another nullable type.
let nullableFloat = Nullable.float nullableInt

// Use the regular non-nullable float operator to convert to a non-nullable float.
printfn $"%f{float nullableFloat}"

输出为 10.000000

对于可为 null 的数据字段(例如 sumByNullable)的查询运算符,也可用于查询表达式。 不可为 null 类型的查询运算符与可以为 null 的类型不兼容,因此在使用可以为 null 的数据值时,必须使用相应查询运算符的可为 null 版本。 有关详细信息,请参阅 查询表达式

以下示例演示如何在 F# 查询表达式中使用可以为 null 的运算符。 第一个查询演示如何在没有可为 null 运算符的情况下编写查询;第二个查询显示使用可为 null 运算符的等效查询。 有关完整上下文,包括如何设置数据库以使用此示例代码,请参阅 演练:使用类型提供程序访问 SQL 数据库

open System
open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
open Microsoft.FSharp.Linq

[<Generate>]
type dbSchema = SqlDataConnection<"Data Source=MYSERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;">

let db = dbSchema.GetDataContext()

query {
    for row in db.Table2 do
    where (row.TestData1.HasValue && row.TestData1.Value > 2)
    select row
} |> Seq.iter (fun row -> printfn $"%d{row.TestData1.Value} %s{row.Name}")

query {
    for row in db.Table2 do
    // Use a nullable operator ?>
    where (row.TestData1 ?> 2)
    select row
} |> Seq.iter (fun row -> printfn "%d{row.TestData1.GetValueOrDefault()} %s{row.Name}")

另请参阅