Dependency injection, Episerver 11, Web API

Dependency injection with Episerver CMS and Web API

This post is about clarifying the configuration you need to do if you wish to use dependency injection in your Episerver project together with Web API.

Episerver CMS uses dependecy injection and it is documented in the CMS developer guide. The documentation covers how you setup the dependency injection for your ASP.NET MVC controllers in the section “Integration with MVC 5 dependency resolver” and it includes the code how to do it using the “IServiceLocator” implementation (Alloy MVC sample site uses also this “default” IServiceLocator implementation which you can always refer to too). IServiceLocator is the abstraction that Episerver uses to de-couple “our” code from the underlying StuctureMap that it currenly uses as the IoC/DI container implementaiton.

All id fine with the “default” implementation but what about if we need Web API and want to use dependency injection with it? Henrik Fransas has already blogged about this at the end of 2016 but back then the documentation was still using the reference implementation that had direct dependecy to StructureMap. That old code is still available if you look at the CMS 9 documentation about dependecy injection. So lets call my blog post a “2019 refresh to what Henrik Fransas already has blogged“.

Episerver CMS 11 and Web API dependecy injection

So if you are using Episerver CMS 11 with the currently suggested “ServiceLocatorDependencyResolver” or you are testing/demoing with Episerver MVC Alloy sample site and add Web API then what are your options to get DI to work with your Web API controllers?

  • Add a new dependency resolver implementation for Web API
  • Remove the exising one and replace it with a StuctureMapDependecyResolver implementation that works with both MVC and Web API

The default ServiceLocatorDependencyResolver can not be used with Web API because it doesn’t expose the underlying container so that we could request a “scope” from it, which we would need in the Web API.

Option: new dependency resolver just for the Web API

For this option we need a new dependency resolver and an implementation of IDependencyScope.

The sample implementation of IDependencyScope below requires NuGet packages:

  • StructureMap (used version 4.7.1)
  • Microsoft.AspNet.WebApi.Core (used version 5.2.7)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Dependencies;
using StructureMap;
namespace EpiWithWebAPI.Web.Business.Dependencies.WebApi
{
public class StructureMapDependencyScope : IDependencyScope
{
// this is a StructureMap specific implementation
private readonly IContainer _container;
public StructureMapDependencyScope(IContainer container)
{
_container = container ?? throw new ArgumentNullException(nameof(container));
}
public object GetService(Type serviceType)
{
ThrowIfDisposed();
if (serviceType == null)
{
return null;
}
if (serviceType.IsAbstract || serviceType.IsInterface)
{
return _container.TryGetInstance(serviceType);
}
return _container.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
ThrowIfDisposed();
return _container.GetAllInstances(serviceType).Cast<object>();
}
private void ThrowIfDisposed()
{
if (disposedValue)
{
throw new ObjectDisposedException(nameof(StructureMapDependencyScope));
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
_container.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~StructureMapScope()
// {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// see: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose
// "If the type has no finalizer, the call to GC.SuppressFinalize has no effect."
// GC.SuppressFinalize(this);
}
#endregion
}
}

Implementation sample for our Web API dependency resolver:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Dependencies;
using StructureMap;
namespace EpiWithWebAPI.Web.Business.Dependencies.WebApi
{
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly IContainer _container;
public StructureMapDependencyResolver(IContainer container)
{
_container = container ?? throw new ArgumentNullException(nameof(container));
}
public IDependencyScope BeginScope()
{
ThrowIfDisposed();
return new StructureMapDependencyScope(_container.GetNestedContainer());
}
public object GetService(Type serviceType)
{
ThrowIfDisposed();
return _container.TryGetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
ThrowIfDisposed();
return _container.GetAllInstances(serviceType).Cast<object>();
}
private void ThrowIfDisposed()
{
if(disposedValue)
{
throw new ObjectDisposedException(nameof(StructureMapDependencyResolver));
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
_container.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~StructureMapDependencyResolver()
// {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// see: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose
// "If the type has no finalizer, the call to GC.SuppressFinalize has no effect."
// GC.SuppressFinalize(this);
}
#endregion
}
}

And then in your Episerver initialization module you would have it registered like the following:


using System.Web.Mvc;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
namespace EpiWithWebAPI.Web.Business.Initialization
{
[InitializableModule]
public class DependencyResolverInitialization : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
// some additional custom registration can be done here
// web api dependency resolver
// full namepsaces used here on purpose to make it clear the dependecy resolver
// code is for Web API and not for MVC
// context.StructureMap() is an extension method in namespace: EPiServer.ServiceLocation
// where from we get the StructureMap IContainer
// The extension method is NuGet package: EPiServer.ServiceLocation.StructureMap version 2.x
// the used dependency resolver is in gist: https://gist.github.com/alasvant/f7c8bc121c7f886a02ab38d2dd45ee5f
// and the IDependencyScope is in gist: https://gist.github.com/alasvant/5f3e3e6b385a7e0236d066316155948c
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new Dependencies.WebApi.StructureMapDependencyResolver(context.StructureMap());
}
public void Initialize(InitializationEngine context)
{
DependencyResolver.SetResolver(new ServiceLocatorDependencyResolver(context.Locate.Advanced));
}
public void Uninitialize(InitializationEngine context)
{
}
public void Preload(string[] parameters)
{
}
}
}

Option: combined dependency resolver

Note! The following samples will use the same StructureMapDependencyScope.cs as used in the above code sample.

So following dependency resolver works both MVC controller and Web API controllers:


using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap;
namespace EpiWithWebAPI.Web.Business.Dependencies.Combined
{
public class StructureMapDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
private readonly IContainer _container;
public StructureMapDependencyResolver(IContainer container)
{
_container = container ?? throw new ArgumentNullException(nameof(container));
}
public object GetService(Type serviceType)
{
ThrowIfDisposed();
if (serviceType == null)
{
throw new ArgumentNullException(nameof(serviceType));
}
if (serviceType.IsInterface || serviceType.IsAbstract)
{
return GetInterfaceService(serviceType);
}
return GetConcreteService(serviceType);
}
private object GetConcreteService(Type serviceType)
{
try
{
// Can't use TryGetInstance here because it won’t create concrete types
return _container.GetInstance(serviceType);
}
catch (StructureMapException)
{
return null;
}
}
private object GetInterfaceService(Type serviceType)
{
return _container.TryGetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
ThrowIfDisposed();
return _container.GetAllInstances(serviceType).Cast<object>();
}
#region System.Web.Http.Dependencies.IDependencyResolver
public System.Web.Http.Dependencies.IDependencyScope BeginScope()
{
ThrowIfDisposed();
return new WebApi.StructureMapDependencyScope(_container.GetNestedContainer());
}
#endregion
private void ThrowIfDisposed()
{
if (disposedValue)
{
throw new ObjectDisposedException(nameof(StructureMapDependencyResolver));
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
_container.Dispose();
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~StructureMapDependencyResolver()
// {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// see: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose
// "If the type has no finalizer, the call to GC.SuppressFinalize has no effect."
// GC.SuppressFinalize(this);
}
#endregion
}
}

And then our initialization module would look like the following:


using System.Web.Mvc;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;
namespace EpiWithWebAPI.Web.Business.Initialization
{
[InitializableModule]
public class DependencyResolverInitialization : IConfigurableModule
{
public void ConfigureContainer(ServiceConfigurationContext context)
{
//Implementations for custom interfaces can be registered here.
// dependecy resolver impl in gist: https://gist.github.com/alasvant/c56c2886c6b85494db98cd2c4ce9379c
// context.StructureMap() is an extension method in namespace: EPiServer.ServiceLocation
// where from we get the StructureMap IContainer
// The extension method is in NuGet package: EPiServer.ServiceLocation.StructureMap version 2.x
var resolver = new Dependencies.Combined.StructureMapDependencyResolver(context.StructureMap());
DependencyResolver.SetResolver(resolver); // MVC
System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = resolver; // Web API
}
public void Initialize(InitializationEngine context) {}
public void Uninitialize(InitializationEngine context) {}
public void Preload(string[] parameters) {}
}
}

Using DI in Web API controller in Episerver project

After implementing one of the above options we can use DI in our Web API controller(s) like the following code sample shows (naturally first configure Web API):


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using EPiServer;
using EPiServer.Core;
namespace EpiWithWebAPI.Web.WebApi
{
[RoutePrefix("routed-api/pagename")]
public class AttributeBasedController : ApiController
{
// NOTE!!!
// These are just simple demos and don't do any checks that real production code should do!
// NOTE!!!
private readonly IContentLoader _contentLoader;
public AttributeBasedController(IContentLoader contentLoader)
{
_contentLoader = contentLoader ?? throw new ArgumentNullException(nameof(contentLoader));
}
[HttpGet, Route("all")]
public IEnumerable<string> GetPageNames()
{
// simple sample to get pages under start page
var pages = _contentLoader.GetChildren<PageData>(ContentReference.StartPage);
return pages.Select(x => x.Name);
}
[HttpGet, Route("byid/{id:int}")]
public string GetPageById(int id)
{
if (id < ContentReference.StartPage.ID)
{
throw new ArgumentOutOfRangeException($"Page id cannot be less than {ContentReference.StartPage}.");
}
return _contentLoader.Get<PageData>(new ContentReference(id))?.Name;
}
}
}

p.s. I have a follow up post coming about the Web API configuration – sometimes the Web API configuration just doesn’t seem to work. For now if you run to issues with Web API configuration then it is most likely about the Episerver add-ons and their initialization modules and order of what module does what. Look at the forum post “Cant get web api to work in EPiServer” in Episerver world and Scotts answer.