Resolving your own and EPiServer’s dependencies without the EPiServer ServiceLocator

I recently got my hands on a new project at work and wanted to see if I could avoid using EPiServer‘s ServiceLocator to resolve the dependencies in my code. From previous projects I was used to being able to inject whatever I needed directly through the constructor, and felt that I wanted to continue doing so.

For instance, my class CanHasCookies implements the interface ICanHasCookies as below. In my Kitteh class I would like to automatically get an instance of it without having to use the EPiServer ServiceLocator.

public interface ICanHasCookies
{
  bool GetPermission();
}

public class CanHasCookies : ICanHasCookies
{
  public bool GetPermission() {return true;}
}

public class Kitteh
{
  private readonly ICanHasCookies _canHasCookies;

  public Kitteh(ICanHasCookies canHasCookies)
  {
    _canHasCookies = canHasCookies;
  }

  public FeedKitteh()
  {
    var _canHasCookies.GetPermission();
    // ..
  }
}

To be able to get the instance as in the code snippet above, we would have to do a couple of things; the first being to add our own configuration to EPiServer’s container having StructureMap scan our assemblies.

Creating an InitializableModule as in the numerous blog posts out there helps us tell StructureMap to scan the current assembly using default conventions. Default conventions means that an interface called ISomething will paired up with a concrete class called Something, and so forth.

StructureMapInitializer.cs

[ModuleDependency(typeof(ServiceContainerInitialization))]
[InitializableModule]
public class StructureMapInitializer : IConfigurableModule
{
  void IConfigurableModule.ConfigureContainer(ServiceConfigurationContext context)
  {
    var container = context.Container;
    container.Configure(x => x.Scan(s =>
    {
      s.AssemblyContainingType<StructureMapInitializer>();
      s.WithDefaultConventions();
      s.LookForRegistries();
    }));

    DependencyResolver.SetResolver(new StructureMapDependencyResolver(context.Container));
  }

  void IInitializableModule.Initialize(InitializationEngine context) { }
  void IInitializableModule.Preload(string[] parameters) { }
  void IInitializableModule.Uninitialize(InitializationEngine context) { }
}

If you have multiple projects in your solution that you’d like to scan, such as a Core and a Web project, you could do it by adding one AssemblyContainingType for each one. LookForRegistries lets us structure configuration into a more logic fashion.

At this point (minus line 22 above setting the resolver), we will be able to use EPiServer’s ServiceLocator to retrieve an instance of ICanHasCookies, but that’s now all we wanted. Adding a dependency resolver like below will take us further.

StructureMapDependencyResolver.cs

public class StructureMapDependencyResolver : IDependencyResolver
{
  private readonly IContainer _container;

  public StructureMapDependencyResolver(IContainer container)
  {
    _container = container;
  }

  public IEnumerable<object> GetServices(Type serviceType)
  {
    return _container.GetAllInstances(serviceType) .Cast<object>();
  }

  public object GetService(Type serviceType)
  {
    return serviceType.IsInterface || serviceType.IsAbstract ?
         GetInterfaceService(serviceType) :
         GetConcreteService(serviceType);
  }

  private object GetInterfaceService(Type serviceType)
  {
    return _container.TryGetInstance(serviceType);
  }

  private object GetConcreteService(Type serviceType)
  {
    return _container.GetInstance(serviceType);
  }
}

Thank you Olga Stern for swooping in and continuing setting this up where I left off :)