Search code examples
c#powershellssas-tabular

Use specific type/assembly when calling a static method - Powershell


I am using a static method:

[Microsoft.AnalysisServices.Tabular.JsonSerializer]::DeserializeDatabase($filePath)

Through my several years of supporting this code, I have learned to load the explicit assemblies, and reference the fqn like:

Add-Type -AssemblyName "Microsoft.AnalysisServices, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"

New-Object -TypeName "Microsoft.AnalysisServices.Tabular.Server, Microsoft.AnalysisServices.Tabular, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91"

Because the sqlserver PowerShell module contains their own versions of the assemblies which are not compatible with the SSAS version we run, so we get into dll hell between the "localapp" versions of the module and the versions that I have explicitly loaded into GAC.

Specifically, the sqlserver module is loading Microsoft.AnalysisServices, Version=Version=18.2.2.0 and because it is not bundled with all of the TOM assemblies, is expecting the same version of Microsoft.AnalysisServices.Tabular.Json to exist in the GAC (which it does not). I can work around this by either finding the v18 version of the assemblies I need and installing them to GAC (although I have found that newer versions are not compatible with the version of SSAS I am running) or by removing the assemblies from the sqlserver modules, thus only using the assemblies I have installed in the GAC.

My question is, when I call a static method, from a static class is there a similar way for me to say which specific assembly/loaded type version/token I want to use for this specific method call in a similar way I do when I use New-Object with the fully qualified name in order to explicitly tell Powershell which assembly/type to use so that it does not try to load the newer version?


Solution

  • Good news!

    It doesn't matter if a concrete type is marked static or not - the type literal syntax remains the same, and you should therefore be able to resolve the method in the correct assembly with:

    [Microsoft.AnalysisServices.Tabular.JsonSerializer, Microsoft.AnalysisServices.Tabular, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91]::DeserializeDatabase($filePath)
    

    Or:

    $typeName = 'Microsoft.AnalysisServices.Tabular.JsonSerializer, Microsoft.AnalysisServices.Tabular, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91'
    
    $($typeName -as [type])::DeserializeDatabase($filePath)
    

    Bonus tip!

    Since assembly-qualified type names can become quite long and distracting when reading through the code, you might want to store the type references needed in one or more variables:

    # Create hashtable
    $ASTabularV14 = @{}
    
    # Use existing public type to obtain assembly reference and populate our hashtable
    $type = [Microsoft.AnalysisServices.Tabular.JsonSerializer, Microsoft.AnalysisServices.Tabular, Version=14.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91]
    $publicTabularTypes = $type.Assembly.GetTypes().Where({
      $_.IsPublic -and -not $_.IsAbstract -and $_.Namespace -eq 'Microsoft.AnalysisServices.Tabular'
    }).ForEach({ $ASTabularV14[$_.Name] = $_ })
    

    Now that we have our own little "type accelarator on steroids", we can reuse it later in the script:

    # ... later on in your script
    
    $deserializedDatabase = $ASTabularV14['Serializer']::DeserializeDatabase($dbData)
    $resultOfSomeServerOperation = $ASTabularV14['Server']::DoStuff()