Option Strict On 和 SQL 可空整数的存储过程输出参数

Option Strict On and SQL stored procedure output parameter of nullable integer

此论坛中有几个主题非常接近于提供我的问题的答案,但不是我所需要的。

我正在 VB.Net 中写作,通过 TableAdapter 和存储过程检索数据。存储过程可以return一个或多个可为空整数形式的输出参数。

这是我正在重新访问和整理的一些旧代码,包括添加 Option Strict On,并且存储过程 return 是一个零,而之前它 return 是一个正确的值。我可以绕过这个问题,但我想了解针对这种情况的“最佳实践”是什么。

这是应用 Option Strict 之前的代码,return 是两个输出参数中的正确值:RetVal(return 代码,定义为枚举)和 UnspecifiedCategoryID(定义作为整数类型)。

Using oCategoriesTableAdapter As New YachtManagementDataSetTableAdapters.tvf_CategoriesTableAdapter

oCategoriesTableAdapter.sp_UpdateUnspecifiedCategory(
   RetVal:=SQLReturn,
   UnspecifiedCategoryID:=oCP.GviUnspecifiedCategorySubCategory,
   UnspecifiedCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameCategory).UnspecifiedEntityID,
   UnspecifiedSubCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameSubCategory).UnspecifiedEntityID,
   VesselID:=oCP.GvoActiveVessel.ID
   )

End Using

使用 Option Strict On 时,如果我只是将两者都转换为整数,使用 CInt 或使用 CType 以消除编译器错误(“Option Strict On 不允许从类型 'Integer?' 缩小到类型 'Integer'"), 那么我将永远有一个零 returned:

Using oCategoriesTableAdapter As New YachtManagementDataSetTableAdapters.tvf_CategoriesTableAdapter

oCategoriesTableAdapter.sp_UpdateUnspecifiedCategory(
    RetVal:=CType(SQLReturn, Integer),
    UnspecifiedCategoryID:=CType(oCP.GviUnspecifiedCategorySubCategory, Integer),
    UnspecifiedCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameCategory).UnspecifiedEntityID,
    UnspecifiedSubCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameSubCategory).UnspecifiedEntityID,
    VesselID:=oCP.GvoActiveVessel.ID
    )

End Using

我可以用这段代码绕过这个问题:

Using oCategoriesTableAdapter As New YachtManagementDataSetTableAdapters.tvf_CategoriesTableAdapter

    oCategoriesTableAdapter.sp_UpdateUnspecifiedCategory(
        RetVal:=CType(SQLReturn, Integer),
        UnspecifiedCategoryID:=TestNullableInteger,
        UnspecifiedCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameCategory).UnspecifiedEntityID,
        UnspecifiedSubCategoryAttributeValueID:=oCP.GvtSystemAttributeNames(gcsSystemAttributeNameSubCategory).UnspecifiedEntityID,
        VesselID:=oCP.GvoActiveVessel.ID
        )

End Using

If TestNullableInteger.HasValue Then

    oCP.GviUnspecifiedCategorySubCategory = TestNullableInteger.Value

Else

    oCP.GviUnspecifiedCategorySubCategory = 0

End If

另一种方法是将 GviUnspecifiedCategorySubCategory 本身的数据类型更改为可为 null 的整数的数据类型,并检查该值是否已 returned。

    Friend Property GviUnspecifiedCategorySubCategory As Integer?
        Get
            Return _lviUnspecifiedCategorySubCategory
        End Get
        Set(Value As Integer?)
            If Value.HasValue Then
                _lviUnspecifiedCategorySubCategory = Value
            Else
                _lviUnspecifiedCategorySubCategory = 0
            End If
        End Set
    End Property

但是,如果存储过程有三个或四个输出参数,那么使用这种方法,重新编码开始变得繁重。 GetValueOrDefault 方法显示了承诺,但使用 TableAdapter,我看不出它是如何工作的。

我总是有可能只见树木不见森林。

如有任何建议,我们将不胜感激。

从评论到问题:

Is the FULL error message "Error BC32029 Option Strict On disallows Narrowing from type 'Integer?' to type 'Integer' in copying the value of 'ByRef' parameter 'RetVal' back to the matching argument."? – TnTinMn

Yes @TnTinMn, that is the correct full message. – Neil Miller

您过于关注代码的用途(数据库交互)而忽略了代码语法。以下代码产生相同的错误消息。

Sub DemoIssue()
  Dim SQLReturn As Integer
  SomeMethod(SQLReturn)
End Sub

Sub SomeMethod(ByRef RetVal As Integer?)
  RetVal = 1
End Sub

请注意,SQLReturn 是一种 Integer 类型,作为参数传递给采用可空整数引用的方法。

如果您单击错误 window 中的 BC32029 来搜索有关该错误的帮助,您可能会发现 Option Strict On disallows narrowing from type 'typename1' to type 'typename2' in copying the value of ByRef parameter 'parametername' back to the matching argument说明:

A procedure call supplies a ByRef argument with a data type that widens to the argument's declared type, and Option Strict is On. The widening conversion is allowed when the argument is passed to the procedure, but when the procedure modifies the contents of the variable argument in the calling code, the reverse conversion is narrowing. Narrowing conversions are not allowed with Option Strict On.

To correct this error

Supply each ByRef argument in the procedure call with the same data type as the declared type, or turn Option Strict Off.

所以您需要做的就是将 SQLReturn 定义为 Integer?

关于:

Ah! @TnTinMn - are you about to suggest changing the AllowDbNull property to False for the Output parameters in the TableAdapter itself? Yes, I've tested that and it works - that's a much better approach, I think. – Neil Miller

这是另一种选择,但您需要了解它起作用的原因是 sp_UpdateUnspecifiedCategory 的 TableAdapter 代码被重写为期望 SQLReturnInteger 类型,所以有``SQLReturndefined as anInteger`

没问题

编辑地址评论:

SQLReturn is defined as an Enum with datatype Integer. I cannot define SQLReturnEnum as type Integer? How would you handle this case if you wanted to take advantage of Intellisense when using the Enum? If I coerce SQLReturn using CInt or CType I will only get a zero returned, even if I were to set the Output parameter property to AllowDbNull to False.

我相信您仍在尝试对发送到方法的参数执行类型转换。

oCategoriesTableAdapter.sp_UpdateUnspecifiedCategory(
    RetVal:=CType(SQLReturn, Integer),...

问题在于 CType(SQLReturn, Integer) 是一个 returns 新值的函数。由于参数是通过引用传递的 (ByRef),因此可以在方法中修改这个新值;此类修改不会传播回 SQLReturn.

假设 SQLReturnEnum 定义如下:

Public Enum SQLReturnEnum As Integer
  [Default]
  A
  B
End Enum

然后通过引用传递一个可为 null 的整数并将其值检索为 SQLReturnEnum 的示例将是:

Sub CopyBackExample()
  Dim byRefSqlReturn As Integer? ' declare a temp variable to be passed ByRef 
  SomeMethod(byRefSqlReturn)
  ' cast the temp variable's value to type SQLReturnEnum
  Dim SQLReturn As SQLReturnEnum = If(byRefSqlReturn.HasValue, CType(byRefSqlReturn, SQLReturnEnum), SQLReturnEnum.Default)
End Sub