选项 Strict On 和 DBNull.Value

Option Strict On and DBNull.Value

我已经开始转换我的应用程序以使用 Option Strict On。我一直在做 CStr,Ctype 等,而且进展顺利。

SQLCommand.Parameters.AddWithValue("@TERMINATE", If(IsNothing(txtEarningsTerminated.DateValue), DBNull.Value, txtEarningsTerminated.DateValue))

然后我点了这个。 txtEarningsTerminated.DateValue 是自定义屏蔽文本框。当它的值为 Nothing 时,我不想在数据库中存储任何内容。但是,它指出

Cannot infer a common type, and Option Strict On does not allow 'Object' to be assumed. 

当我将 DBNull.Value 更改为 "" 或什么都不更改时,错误消失了。然而,作为 Nothing,它在运行时失败并显示

The parameterized query '(@CONTROL int,@CLIENTCODE nvarchar(10),@NoBill int,@TERMINATE nv' expects the parameter '@TERMINATE', which was not supplied.

我想在数据库中放入一个 NULL。这个值可以是日期然后变成NULL。

我该如何翻译才能在 Option Strict On 下不产生错误?

这是一种方法(假设您在数据库中使用存储过程):

  1. 在您调用的存储过程中,将日期输入参数设置为 null,使其成为默认值为 null 的可选字段 (@terminate DATETIME = NULL)。

这样,如果您不在过程调用中包含该参数,过程就不会失败。

  1. 将日期字段的默认值设置为 DateTime.MinValue,以便它始终具有一些值。然后你可以测试它是否等于 DateTime.MinValue 以外的日期。如果它是有效的日期值,请添加该行以将日期参数包含到过程调用中。如果它等于 DateTime.MinValue,则不要将参数添加到调用中。

原因是因为运算符 If(condition, executeAndReturnIfTrue, executeAndReturnIfFalse) 期望 truefalse 表达式都是 return 相同的类型。
在您的情况下,如果结果为假,您 return DbNull 键入 String(或您未提及的其他类型)。

如果更明确地创建 SqlParameter,则可以使用下一种方法:

Dim value As Object = Nothing
If txtEarningsTerminated.DateValue Is Nothing Then
    value = DbNull.Value
Else
    value = xtEarningsTerminated.DateValue
End If

Dim param As SqlParameter = New SqlParameter With
{
    .ParameterName = "@TERMINATE",
    .SqlDbType = SqlDbType.VarChar, 'Or use your correct type
    .Value = value
}

如评论中所述,使用 AddWithValue 将强制 ADO.NET 自动为您的值确定 sql 类型,这可能会导致不同的问题。例如,每次您 运行 使用不同值的相同查询时,每次更改值时您的查询计划都会重新编译。

您可以为 string 创建扩展方法

Public Module ExtensionsModule
    Public Function DbNullIfNothing(this As String) As Object
        If this Is Nothing Then Return DBNull.Value
        Return this
    End Function
End Module

然后用它代替你的 "inline If method"

Dim value As String = Nothing
Dim param As SqlParameter = New SqlParameter With
{
    .ParameterName = "@TERMINATE",
    .SqlDbType = SqlDbType.VarChar,
    .Value = value.DbNullIfNothing()
}

所以答案很明显,而且在错误中。我只需要将 DBNull.Value 封装在 Ctype 对象

CType(DBNull.Value, Object)

然后代码就可以正常编译了。