<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1639164799743833&amp;ev=PageView&amp;noscript=1">
Diagram Views

Creating Episerver NuGet Packages

Brad McDavid
#Episerver, #Code
Published on April 6, 2018
warren-wong-323107-unsplash-1

Episerver has a great developer community with excellent, helpful folks. Many of these wonderful people also author community NuGet packages for Episerver which provide multiple functionalities. In this post, we're going to discuss some components of how to make a NuGet package.

Episerver has a great developer community with excellent, helpful folks. Many of these wonderful people also author community NuGet packages for Episerver which provide functionality such as:

At Diagram, we rely on many of these packages in our client projects, to help them truly shine. Today I would like to discuss some components of how to make a NuGet package, and hopefully get more package authors started in the Episerver developer community.

Best Practices

When creating NuGet packages there are a few key factors to follow for best practices. The first important factor is only depending on what the package actually needs. From my experience in creating Episerver packages, I've found I usually only need to install one of the following:

  • EPiServer.Framework
  • EPiServer.CMS.Core
  • EPiServer.CMS.UI.Core
  • EPiServer.Commerce.Core

There are dependencies between the above packages, such as EPiServer.CMS.UI.Core also installs EPiServer.CMS.Core and EPiServer.Framework, but in terms of a creating a NuGet package, the only dependency that matters is the highest one. The version of this dependency is another important factor. The best practice on choosing a version is to choose the lowest version which contains the functionality needed. One example is if Episerver adds a feature in EPiServer.CMS.UI.Core 11.6 which your package requires, then that is lowest version you package can use. Episerver packages follow semantic versioning where major versions contain breaking changes, minor versions add new features, and patches fix bugs. In general, most packages can depend on the lowest version minor version of the dependency, such as EPiServer.CMS.Core 11.0.

Code extensibility is another important factor. Provide abstractions with good defaults registered to Episerver's DI container, but allow others to provider their own implementation if the default doesn't meet their needs. The default implementations can be registered using the ServiceConfigurationAttribute or by explicitly assigning services in an IConfigurableModule. In the default implementations, utilize constructor injection to provide any dependency the service may need. Also, by using constructor injection you will make it easier to provide unit tests for your code to ensure it behaves appropriately or throws the correct exceptions.

Perhaps the most important thing to do is letting others know how to use the package. Documentation is often an overlooked component, but NuGet packages have the ability to include a project URL and even open a readme.txt file when the package is installed. This file can be used to provide some very important information such as where to configure parameters like credentials for connecting to external services, code extension points to make customizations, and general package usage. Also including brief summary of release notes is a nice way to let package consumers know what has changed.

Things to avoid

Episerver 11 brought a big shift in terms dependency injection / inversion-of-control and configuration. While still utilizing Structuremap as their choice of a DI container, they now require an additional package EPiServer.ServiceLocation.StructureMap to access the Structuremap container. Currently this package supports two different versions of Structuremap, where the 1.x is the older signed version and the 2.x has the newer unsigned version. If a package uses any version of EPiServer.ServiceLocation.StructureMap as a dependency, it will create headaches, especially true if the older 1.x version is used. Community Nuget packages for Episerver should avoid installing this as a dependency, as most things it provides can be worked around in a few ways.

The first work around is to decorate your services with the ServiceConfigurationAttribute.

[ServiceConfiguration(ServiceType = typeof(IService), Lifecycle = ServiceInstanceScope.Singleton)]
public class ServiceImpl{}

This approach is the simplest as service is discovered and registered during Episerver's initialization process. Another approach is to assign services in an IConfigurableModule using the IServiceConfigurationProvider Services property on configure context.

[InitializableModule]
public class ConfigureServices : IConfigurableModule
{
    public void ConfigureContainer(ServiceConfigurationContext context)
    {
        //Register IService1 with implementation Service1, create new instance every time
        context.Services.AddTransient<IService, Service>();
        //Register IService1 with implementation Service1, re-use a single instance
        context.Services.AddSingleton<IService, Service>();
    }
    public void Initialize(InitializationEngine context)
    {
    }
    public void Uninitialize(InitializationEngine context)
    {
    }
}

Side rant

Speaking of dependency injection, there are two things that I wanted to mention to avoid, namely:

// usages of
EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<T>()

// and 
public Injected<T> Service { get; set; }

In most cases these can be replaced by using constructor injection and letting the DI/IOC container provide them. If you find yourself in a case where constructor injection cannot be used, choose the Injected<T> version and be sure to make it public with a getter and setter, to allow others to change the dependency if needed. Using the ServiceLocator in code often leads to hidden dependencies and can make code more difficult to unit test.

Summary

Beyond the things listed above, I like to also use tooling to provide things such as build and source control information in the compiled dlls, zip module folders for Episerver during build process, and use a build server to do the NuGet package creation. Appveyor and Visual Studio Team Services are great building platforms that can offer free builds within limits.

Finally, I use the new ASP.Net standard for creating NuGet packages as it allows for projects to target multiple ASP.NET platforms such as ASP.Net Framework 4.6.1 and .NET Standard 2.0 during the same build. While ASP.Net 4.6.1 and NetStandard2.0 are identical at the API level, I still include both which allows NuGet to prefer the full framework install in older project formats. For help in determining which target frameworks to support, the Get Nearest Framework tool can shed light on how NuGet chooses the package during install.

Bonus

The new dotnet command line interface (CLI) provides a very powerful templating mechanism using the dotnet new templateName format. One example is to create a new ASP.Net Core web application using the following command:

dotnet new web

This creates all the necessary code to build and run a starter web application.

During the process of writing this blog, I thought, wouldn't having a template to jumpstart a NuGet package for Episerver be very useful? I've gotten the ball rolling, by creating such a template. To install this template, open powershell and run:

dotnet new --install "bmcdavid.NuGetTemplate.EpiserverPackage::*"

Then to create an instance of the template make a new folder using:

mkdir folderName
cd folderName

And finally the best part run:

dotnet new epipackage

This creates a barebones ASP.NET Standard project setup to target both ASP.Net 4.6.1 and .NET Standard 2.0. During the template installation, there is a post action to run a setup script which should be used to rename the EpiserverTemplate placeholder string with a name of your choosing. After installation, you can run commands such as:

  • dotnet build - builds the solution
  • dotnet test - runs the unit tests
  • dotnet pack - the important packing command to create nuget packages

Additional templates can also be displayed using the following command:

dotnet new -l

Which should display something similar to:

dotnet-new-list

 

There is still much room for improvement, since not much code is provided in the template itself, just the basics to get started. The current template features include:

  • Zipping an Episerver module folder (if enabled in csproj) during build
  • Adds readme.md as readme.txt for displaying instructions on package install
  • Adding releasenotes.txt to package information during the package step.
  • Adds build information like git commit hash and branch to the compiled dll.

If you would like to help improve the template, the code is available on Github.

A final note, to restore packages from Episerver in the dotnet CLI, you may need to add the Episerver feed to your global NuGet configuration file with the below:

nuget sources Add -Name "Episerver" -Source https://nuget.episerver.com/feed/packages.svc/