Tuesday, January 3, 2017

Use specific version of Sharepoint client object model in PowerShell via assembly binding redirection

Recently we faced with interesting problem: in PowerShell script we needed to use OfficeDevPnP.Core.dll of version which was built with client-side object model (CSOM) version 16.1.3912.1204. However in our basic app we used newer CSOM version 16.1.5625.1200 and since its dlls were added to source control together with other source files we wanted to reuse it in PowerShell scripts as well. In application it was easy to fix issue with different referenced CSOM version by specifying assembly binding redirection in app.config. But in PowerShell it was not so simple. By default all attempts to call methods from OfficeDevPnP.Core.dll threw exception that Microsoft.SharePoint.Client.dll of version 16.1.3912.1204 is not found.

Error is logical because as I wrote above our version of OfficeDevPnP.Core.dll was built with older CSOM version. But how to solve it in PowerShell? In order to avoid this error we can use code-based assembly redirection binding:

   1: $currentDir = Convert-Path(Get-Location)
   2: $dllsDir = resolve-path($currentDir + "\..\csom16.1.5625.1200\")
   3: $officeDevPnPDir = resolve-path($currentDir + "\..\OfficeDevPnPCore16.1.8.1115.0\lib\net45\")
   5: $AssemblyClientRuntime =
   6:     [System.Reflection.Assembly]::LoadFile(
   7:         [System.IO.Path]::Combine($dllsDir, "Microsoft.SharePoint.Client.Runtime.dll"))
   8: $AssemblyClient =
   9:     [System.Reflection.Assembly]::LoadFile(
  10:         [System.IO.Path]::Combine($dllsDir, "Microsoft.SharePoint.Client.dll"))
  12: $OnAssemblyResolve = [System.ResolveEventHandler] {
  13:     param($sender, $e)
  15:     if ($e.Name -eq "Microsoft.SharePoint.Client.Runtime, Version=16.1.3912.1204, " +
  16:         "Culture=neutral, PublicKeyToken=71e9bce111e9429c")
  17:     {
  18:         return $AssemblyClientRuntime
  19:     }
  20:     if ($e.Name -eq "Microsoft.SharePoint.Client, Version=16.1.3912.1204, " +
  21:         "Culture=neutral, PublicKeyToken=71e9bce111e9429c")
  22:     {
  23:         return $AssemblyClient
  24:     }
  26:     foreach($a in [System.AppDomain]::CurrentDomain.GetAssemblies())
  27:     {
  28:         if ($a.FullName -eq $e.Name)
  29:         {
  30:             return $a
  31:         }
  32:     }
  33:     return $null
  34: }
  35: [System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

In this code at first we load specific versions of CSOM and OfficeDevPnP.Core (lines 1-10). Then we define custom assembly binding resolver (lines 12-34) and add it to current AppDomain’s assembly resolvers (line 35). In custom resolver we check assembly name and if it corresponds to older version of CSOM 16.1.3912.1204 we resolve it with current newer version 16.1.5625.1200. This approach allows to use specific CSOM version in PowerShell.

Also need to mention that when you launch Sharepoint Management Shell it loads currently installed CSOM from the GAC and in .Net there is no way to unload assembly once it is already loaded. I.e. if you need to use specific CSOM version in PowerShell which differs from the version installed into your GAC, run Windows PowerShell console instead of Sharepoint Management Shell.

No comments:

Post a Comment