Search code examples
vb.net.net-standard

VB.NET library breaks on .Net Standard 2.1 when migrated from 2.0 - Requested operation is not avalaible on TryParse


There is a legacy Visual Basic class library targetting .Net Standard 2.0 currently:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.VisualBasic" Version="10.3.0" />
  </ItemGroup>
</Project>

There is also a class inside

Public Class Class1
  Public Shared Function TryGetInteger(ByVal value As Object) As Integer
    Dim out As Integer

    If Integer.TryParse(value, out) Then
      Return out
    End If

    Return Integer.MinValue
  End Function

  Public Shared Function BooleanToInt(ByVal bValue As Object) As Integer
    If (bValue = DBNull.Value) Then
      Return 0
    ElseIf bValue = True Then
      Return 1
    Else
      Return 2
    End If
  End Function
End Class

At this point everything works fine.

But changing <TargetFramework>netstandard2.0</TargetFramework> to 2.1 breaks the build with the following error in the Class1.TryGetInteger function:

Requested operation is not available because the runtime library function 'Microsoft.VisualBasic.CompilerServices.Conversions.ChangeType' is not defined

How can this be fixed?

EDIT: Please disregard below idea:

The first idea comes from this closed github issue, that suggests adding <VBRuntime>Default</VBRuntime> to the project file, but this results in error:

could not find library 'Microsoft.VisualBasic.dll'

But it is already linked as a package... Any ideas?

EDIT: As mentioned is the comments, my take on the linked github issue is a bit misleading, so I guess I'm back to the beginning with the Requested operation is not available error.


Solution

  • Integer.TryParse() expects a String, but the input argument type is Object.

    You can fix the issue like this:

    Public Shared Function TryGetInteger(value As Object) As Integer
        Dim out As Integer
        If Integer.TryParse(value.ToString(), out) Then Return out
    
        Return Nothing 'Same as Integer.MinValue in this context
    End Function
    

    The extra ToString() call is the same thing the old framework was doing for you implicitly. Now you're aware of it.

    But really I'd do this:

    Public Shared Function TryGetInteger(value As String) As Integer
        Dim out As Integer
        If Integer.TryParse(value, out) Then Return out
    
        Return Nothing
    End Function
    

    Which will probably bring up a whole new set of compiler errors, but there will be value in going back and fixing them at those calling locations. Most of the errors can be fixed by adding .ToString() to the end of value when calling the function, and any that can't were almost certainly hiding bugs.

    Similarly for the BooleanToInt() function, I'd use explicit types, and maybe overloading to get better results, like this:

    Public Shared Function BooleanToInt(bValue As DBNull) As Integer
        Return 0 'If this overload was selected, we know we want 0
    End Function
    Public Shared Function BooleanToInt(bValue As Boolean) As Integer
        If bValue Then Return 1
        Return 2
    End Function
    Public Shared Function BooleanToInt(bValue As Object) As Integer
        Return 2
    End Function
    

    As a rule of thumb, anywhere you need to use the Object type directly is a magnet for bugs. It's a smell in the code and something to avoid.