There are several topics in this forum that come tantalisingly close to providing an answer to my question, but not quite what I need.
I am writing in VB.Net, retrieving data via TableAdapters and stored procedures. The stored procedures can return one or more Output parameters in the form of nullable integers.
This is some old code that I am revisiting and tidying up, including the addition of Option Strict On, and the Stored Procedure returns a zero where previously it returned a correct value. I can circumvent the problem but I would like to understand what "best practice" dictates for this circumstance.
This is the code before Option Strict was applied, and returns the correct value in the two Output parameters: RetVal (the return code, defined as an Enum) and UnspecifiedCategoryID (defined as an integer type).
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
With Option Strict On, if I simply cast both to an integer, using CInt or use CType in order to remove the compiler error ("Option Strict On disallows narrowing from type 'Integer?' to type 'Integer'"), then I will always have a zero 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
I can circumvent the problem using this code:
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
Another alternative is to change the data type of GviUnspecifiedCategorySubCategory itself to that of a nullable integer and check there as to whether a value has been 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
However, if the stored procedure has three or four Output parameters then, using this approach, the recoding starts to become onerous. The GetValueOrDefault method shows promise but, using a TableAdapter, I cannot see how this would work.
There's always the chance that I've stopped seeing the wood for the trees.
Any suggestions would be much appreciated.
From the comments to the question:
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
You are too focused on purpose of the code (database interaction) while ignoring the code syntax. The following code produces the same error message.
Sub DemoIssue()
Dim SQLReturn As Integer
SomeMethod(SQLReturn)
End Sub
Sub SomeMethod(ByRef RetVal As Integer?)
RetVal = 1
End Sub
Note that SQLReturn
is a Integer
type being passed as an argument to a method that takes a reference to a nullable integer.
If you would have clicked on the BC32029 in the error window to search for help on the error, you likely would have found Option Strict On disallows narrowing from type 'typename1' to type 'typename2' in copying the value of ByRef parameter 'parametername' back to the matching argument that explains:
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.
So all you need to do is define SQLReturn
as Integer?
Regarding:
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
That is another option, but you need to understand the reason why it works is that TableAdapter code for sp_UpdateUnspecifiedCategory
is rewritten to expect an Integer
type for SQLReturn
so there is no issue with ``SQLReturndefined as an
Integer`
Edit To address comment:
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.
I believe that you are still trying to perform the type conversion on the argument sent to the method.
oCategoriesTableAdapter.sp_UpdateUnspecifiedCategory(
RetVal:=CType(SQLReturn, Integer),...
The problem with this is that CType(SQLReturn, Integer)
is a function that returns a new value. As the argument is passed by reference (ByRef
) it is this new value that can be modified in the method; such modification is not propagated back to SQLReturn
.
Assuming SQLReturnEnum
is defined like:
Public Enum SQLReturnEnum As Integer
[Default]
A
B
End Enum
Then an example of passing by reference a nullable Integer and retrieving its value as a SQLReturnEnum
would be:
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