4 Dec 2015

Sharing Code Between SharePoint Solutions

I'm currently working on upgrading a large set of SharePoint Web Part solutions from SharePoint 2010 to SharePoint 2013. As part of this work, I have been identifying code that is common to several of the solutions (logging to the ULS, decoding Claims Based Authenticated usernames) and moving it to a single common utility DLL. I thought it would be useful to document how I have done this, and to share some lessons I've learnt along the way.

NuGet

"NuGet is the package manager for the Microsoft development platform including .NET. " Taken from https://www.nuget.org

NuGet is the obvious choice for sharing a common assembly between multiple solutions. By simply adding this NuGet package (https://www.nuget.org/packages/CreateNewNuGetPackageFromProjectAfterEachBuild/) to the Visual Studio project I created for my common code, a .nupkg file was automatically created following each build.  I created a local (internal) package repository on a network share and added a post-build event to my project to push new .nupkg files to this location. Altogether, this took approximately an hour to add to my existing common utility project, and most of this was learning how to populate the NuGet package manifest.

I then added the local package source as a feed in Visual Studio (see https://docs.nuget.org/create/hosting-your-own-nuget-feeds for details), and installed the package in the various SharePoint solutions I was working on.  To ensure that I could track what version of the common utility DLL was installed, the assembly and resulting .nupkg file was versioned. As the upgrading of the solutions is ongoing, there will be a need to add additional common code to the utility assembly, and to ensure that all SharePoint solution are updated to the latest version.

Packaging and Deployment

My initial approach to package and deploy the common utility assembly was to add it as an additional assembly into the various SharePoint solution packages, (see https://msdn.microsoft.com/en-us/library/ee231595%28v=vs.120%29.aspx for details). This is the recommended approach from MSDN. It is also completely wrong.

The problem is that SharePoint packaging and deployment mechanism does not count the number of references that are made to shared assemblies used across multiple SharePoint solutions. This leads to an issue when a solution referencing the shared assembly is uninstalled, then the shared assembly is also uninstalled. This leads to the other solutions that reference the shared assembly being broken. This isn’t a new problem – it has been around since SharePoint 2010 (see http://lawrencecawood.com/2011/07/13/how-i-solved-the-problem-of-dlls-being-removed-during-wsp-uninstallation/).

In my specific case, the problem occurred when I deployed a SharePoint solution that included a new updated version of the common assembly. If I had deployed several solutions that referenced version 1.0.0.0 of the Common.dll, and then deployed an upgraded solution that referenced version 1.0.0.1 of the Common.dll, the version 1.0.0.0 Common.dll was uninstalled as part of the deployment and version 1.0.0.1 was installed instead. The new solution worked perfectly, but the other deployed solutions that still referenced version 1.0.0.0 were broken. This lead to a sticky moment following one deployment to Production. Once I twigged what the issue was, I simply redeployed the most recent WSP of a solution containing the now missing version of the common assembly.

Deployment Redux

It was clear that continuing to deploy the common assembly as an additional assembly in he SharePoint solution package wasn't an option. While it was an intermittent issue, only occurring when deploying an updated version of the common assembly, it was still a problem that could lead to unnecessary downtime. So, what other options for sharing common code were there?

After a bit of research, I came up with the following options:

  1. Deploy each new version of the Common.dll as a separate solution, deployed separately. The WSP for each version would include the version number in the name (i.e. Common.V1.0.0.1.dll) and would ned to have unique solution GUIDs for deployment. This would allow the referenced assembly to be removed from the package for web parts that reference it, so it would not be removed when deploying newer versions.
  2. Create a separate solution package to deploy the various versions of the common assembly, and several other 3rd party DLLs. This would isolate the deployment of the common assembly from the deployment of the various solutions that reference it. Obviously, if a solution referencing a new (not previously deployed) version of the common assembly is now deployed, the new solution containing the common assemblies must be updated to include the new version and also deployed.
  3. Remove versioning entirely. Simply use V1.0.0.0 of the common assembly. Instead of deleting methods, or changing method parameters, future changes would require overload existing methods and using the [Deprecated] and [Obsolete] attributes to indicate methods that have been superseded in the latest 'version' of the assembly.
  4. Merge the common assembly into a single assembly for each separate web part solution using ILMerge. This allows for the versioned common assembly be used, and results in a single DLL for each solution, avoiding additional assemblies and versioning in SharePoint altogether.

Clearly, using ILMerge was the best way forward. And it turned out to be very easy to implement with the existing solutions. The steps are:

  1. Install the Nuget package https://www.nuget.org/packages/MSBuild.ILMerge.Task/1.0.3-rc2 (it will also install a NuGet package for ILMERGE as a dependency).
  2. Clean and rebuild the solution in Visual Studio.
The resulting DLL takes the name of the parent solution, and when you check the assembly using ILSpy, it includes the namespace of the common utility code as well as that of the parent solution. Note, if you are building an assembly for a SharePoint solution, all the child assemblies must be signed.  The additional assemblies to be combined with the primary DLL must also have the Copy Local property set to TRUE (this is the default when using NuGet packages).

Hopefully, the above is of some use if you're looking for a solution to share code across multiple SharePoint solutions.