Wednesday, September 28, 2011

Assembly binding redirect in Sharepoint 2010: how old code for SP 2007 works in SP 2010

One of the nice features of Sharepoint 2010 is the automatic assemblies redirect which allows to use old code which was written and compiled for Sharepoint 2007 also in Sharepoint 2010. It is achieved by assemblies redirects in web.config. Let’s see what bindings are added when you create new web application:

   1: <runtime>
   2:   <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
   3:     <dependentAssembly>
   4:       <assemblyIdentity name="Microsoft.SharePoint.Dsp" publicKeyToken="71e9bce111e9429c" culture="neutral" />
   5:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
   6:     </dependentAssembly>
   7:     <dependentAssembly>
   8:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.OleDb" publicKeyToken="71e9bce111e9429c" culture="neutral" />
   9:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  10:     </dependentAssembly>
  11:     <dependentAssembly>
  12:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.SoapPT" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  13:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  14:     </dependentAssembly>
  15:     <dependentAssembly>
  16:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.Sts" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  17:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  18:     </dependentAssembly>
  19:     <dependentAssembly>
  20:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.XmlUrl" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  21:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  22:     </dependentAssembly>
  23:     <dependentAssembly>
  24:       <assemblyIdentity name="Microsoft.SharePoint.intl" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  25:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  26:     </dependentAssembly>
  27:     <dependentAssembly>
  28:       <assemblyIdentity name="Microsoft.SharePoint.Library" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  29:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  30:     </dependentAssembly>
  31:     <dependentAssembly>
  32:       <assemblyIdentity name="Microsoft.SharePoint.Security" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  33:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
  34:     </dependentAssembly>
  35:     <dependentAssembly>
  36:       <assemblyIdentity name="Microsoft.SharePoint.Dsp" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  37:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  38:     </dependentAssembly>
  39:     <dependentAssembly>
  40:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.OleDb" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  41:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  42:     </dependentAssembly>
  43:     <dependentAssembly>
  44:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.SoapPT" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  45:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  46:     </dependentAssembly>
  47:     <dependentAssembly>
  48:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.Sts" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  49:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  50:     </dependentAssembly>
  51:     <dependentAssembly>
  52:       <assemblyIdentity name="Microsoft.SharePoint.Dsp.XmlUrl" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  53:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  54:     </dependentAssembly>
  55:     <dependentAssembly>
  56:       <assemblyIdentity name="Microsoft.SharePoint.intl" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  57:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  58:     </dependentAssembly>
  59:     <dependentAssembly>
  60:       <assemblyIdentity name="Microsoft.SharePoint.Library" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  61:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  62:     </dependentAssembly>
  63:     <dependentAssembly>
  64:       <assemblyIdentity name="Microsoft.SharePoint.Security" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  65:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  66:     </dependentAssembly>
  67:     <dependentAssembly>
  68:       <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35" />
  69:       <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0" />
  70:     </dependentAssembly>
  71:     <qualifyAssembly partialName="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=neutral" fullName="Microsoft.SharePoint.WorkflowActions, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  72:     <qualifyAssembly partialName="Microsoft.SharePoint.WorkflowActions, Version=14.0.0.0, Culture=neutral" fullName="Microsoft.SharePoint.WorkflowActions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
  73:     <probing privatePath="bin;_app_bin" />
  74:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  75:       <assemblyIdentity name="Microsoft.Office.Workflow.Routing" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  76:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  77:     </dependentAssembly>
  78:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  79:       <assemblyIdentity name="Microsoft.Office.Workflow.Tasks" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  80:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  81:     </dependentAssembly>
  82:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  83:       <assemblyIdentity name="Microsoft.Office.Policy" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  84:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  85:     </dependentAssembly>
  86:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  87:       <assemblyIdentity name="Microsoft.Office.Workflow.Feature" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  88:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  89:     </dependentAssembly>
  90:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  91:       <assemblyIdentity name="Microsoft.Office.Excel.WebUI" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  92:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  93:     </dependentAssembly>
  94:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  95:       <assemblyIdentity name="Microsoft.Office.infopath" publicKeyToken="71e9bce111e9429c" culture="neutral" />
  96:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
  97:     </dependentAssembly>
  98:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
  99:       <assemblyIdentity name="Microsoft.Office.infopath.server" publicKeyToken="71e9bce111e9429c" culture="neutral" />
 100:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
 101:     </dependentAssembly>
 102:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
 103:       <assemblyIdentity name="Microsoft.Office.Server" publicKeyToken="71e9bce111e9429c" culture="neutral" />
 104:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
 105:     </dependentAssembly>
 106:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
 107:       <assemblyIdentity name="Microsoft.Office.Server.UI" publicKeyToken="71e9bce111e9429c" culture="neutral" />
 108:       <bindingRedirect oldVersion="12.0.0.0" newVersion="14.0.0.0" />
 109:     </dependentAssembly>
 110:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
 111:       <assemblyIdentity name="Microsoft.ReportingServices.SharePoint.UI.WebParts" publicKeyToken="89845dcd8080cc91" culture="neutral" />
 112:       <bindingRedirect oldVersion="9.0.242.0" newVersion="10.0.0.0" />
 113:     </dependentAssembly>
 114:     <dependentAssembly xmlns="urn:schemas-microsoft-com:asm.v1">
 115:       <assemblyIdentity name="Microsoft.SharePoint.Portal" publicKeyToken="71e9bce111e9429c" culture="neutral" />
 116:       <bindingRedirect oldVersion="11.0.0.0" newVersion="14.0.0.0" />
 117:     </dependentAssembly>
 118:   </assemblyBinding>
 119: </runtime>

Quite obvious. However it doesn’t contain bindings for 2 most frequently used assemblies:

  • Microsoft.SharePoint.dll
  • Microsoft.SharePoint.Publishing.dll

Nevertheless these assemblies are also redirected from “12.0.0.0” version to “14.0.0.0” version. How Sharepoint performs this redirection, if there are no bindings in web.config file?

First of all why I’m sure that mentioned 2 assemblies are also redirected? Because our open source library Camlex.Net successfully works both in Sharepoint 2007 and Sharepoint 2010 without recompilations. It has reference to Microsoft.SharePoint.dll “12.0.0.0” version.

So there should be other way to achieve assembly binding redirection – which doesn’t rely on web.config. In order to investigate it I created simple application layouts page and added a call to Camlex method on it:

   1: <%@ Page Language="C#" %>
   2: <%@ Assembly Name="Camlex.NET, Version=2.3.0.0, Culture=neutral, PublicKeyToken=831792d54d5285b7" %>
   3: <%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
   4: <%@ Import Namespace="CamlexNET" %>
   5:  
   6: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   7:  
   8: <html xmlns="http://www.w3.org/1999/xhtml">
   9: <head runat="server">
  10:     <title></title>
  11:     <script runat="server">
   1:  
   2:         protected override void OnLoad(EventArgs e)
   3:         {
   4:             this.lbl.Text = HttpUtility.HtmlEncode(Camlex.Query().ViewFields(x => x["Foo"]));
   5:         }
   6:     
</script>
  12: </head>
  13: <body>
  14:     <form id="form1" runat="server">
  15:     <div>
  16:         <asp:Label ID="lbl" runat="server" />
  17:     </div>
  18:     </form>
  19: </body>
  20: </html>

Before to access this page in context of Sharepoint 2010 site I ran fuslogvw.exe. It helped to get the answer. Here is the log:

   1: <meta http-equiv="Content-Type" content="charset=unicode-1-1-utf-8"><!-- saved from url=(0015)assemblybinder: --><html><pre>
   2: *** Assembly Binder Log Entry  (28.9.2011 @ 21:52:25) ***
   3:  
   4: The operation was successful.
   5: Bind result: hr = 0x0. The operation completed successfully.
   6:  
   7: Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
   8: Running under executable  c:\windows\system32\inetsrv\w3wp.exe
   9: --- A detailed error log follows. 
  10:  
  11: === Pre-bind state information ===
  12: LOG: User = ...
  13: LOG: DisplayName = Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c
  14:  (Fully-specified)
  15: LOG: Appbase = file:///C:/inetpub/wwwroot/wss/VirtualDirectories/test80/
  16: LOG: Initial PrivatePath = C:\inetpub\wwwroot\wss\VirtualDirectories\test80\bin
  17: LOG: Dynamic Base = C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\126602ad
  18: LOG: Cache Base = C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Temporary ASP.NET Files\root\126602ad
  19: LOG: AppName = 6d85c603
  20: Calling assembly : (Unknown).
  21: ===
  22: LOG: This bind starts in default load context.
  23: LOG: Using application configuration file: C:\inetpub\wwwroot\wss\VirtualDirectories\test80\web.config
  24: LOG: Using host configuration file: C:\Windows\Microsoft.NET\Framework64\v2.0.50727\Aspnet.config
  25: LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
  26: LOG: Publisher policy file is found at C:\Windows\assembly\GAC_MSIL\Policy.12.0.Microsoft.SharePoint\14.0.0.0__71e9bce111e9429c\Policy.12.0.Microsoft.SharePoint.config.
  27: LOG: Publisher policy file redirect is found: 12.0.0.0 redirected to 14.0.0.0.
  28: LOG: ProcessorArchitecture is locked to MSIL.
  29: LOG: Post-policy reference: Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL
  30: LOG: Binding succeeds. Returns assembly from C:\Windows\assembly\GAC_MSIL\Microsoft.SharePoint\14.0.0.0__71e9bce111e9429c\Microsoft.SharePoint.dll.
  31: LOG: Assembly is loaded in default load context.
  32:  
  33: </pre></html>

Answer is on lines 26-27. Sharepoint uses publisher policy files technique in order to redirect binding of these assemblies. This is rarely used technique comparing with web.config modifications. I recommend the following article which gives the basic understanding how it works: Using publisher policy assemblies.

E.g. that’s how publisher policy file looks like for Microsoft.SharePoint.dll (as shown in log it is located in C:\Windows\assembly\GAC_MSIL\Policy.12.0.Microsoft.SharePoint\14.0.0.0__71e9bce111e9429c folder and has “Policy.12.0.Microsoft.SharePoint.config” name):

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:    <runtime>
   4:       <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
   5:        <dependentAssembly>
   6:          <assemblyIdentity name="Microsoft.SharePoint"
   7:                            publicKeyToken="71e9bce111e9429c"
   8:                            culture="neutral" />
   9:          <!-- Redirecting to version 14.0.0.0 of the assembly. -->
  10:          <bindingRedirect oldVersion="12.0.0.0"
  11:                           newVersion="14.0.0.0" />
  12:        </dependentAssembly>
  13:       </assemblyBinding>
  14:    </runtime>
  15: </configuration>

That’s how Sharepoint 2010 redirects assemblies binding from 2007 version.