The new Inversion of Control pattern introduced with EPiServer 7 is, in my opinion, one of the best new features of EPiServer CMS. Anywhere in your application you can simply call the ServiceLocator and GetInstance <ISomeInterface>() and it will return the concrete implementation of your choosing of that interface.

This is of course nothing new, StructureMap has been around for years and there are dozens of other excellent frameworks for IoC.

However, in the EPiServer world this is still kind of new and I’ve seen several epi-projects where the concept of IoC is still largely misunderstood. But this is not a post about IoC as such, but if you’re interested here’s an introduction to StructureMap, it’s a pretty old post but still interesting.

I was working in a project recently where we created a couple of concrete implementations for cache handling. A CacheManager and a NullCacheManager, the CacheManager utilized the HttpRuntime.Cache in the background, the NullCacheManager simply cached nothing as one would expect. They both implement the ICacheManager interface, shown highly simplified below.

public interface ICacheManager {

 GetCachedItem<T>(string key, Func<T> uncachedMethod);

 SetCachedItem<T>(string key, T item);

 RemoveCachedItem(string key); 

}

This was all good and we could now easily decouple our design using code looking something like this.

public void SomeMethod() {

 var cacheManager = ServiceLocator.Current.GetInstance<ICacheManager>();

 var cachedItem = cacheManager.GetCachedItem<SomeObject>("key", SomeMethodThatFetchesSomObjectUnCached);

}

This is great and gives us a very loosely coupled design. We could easily create a new concrete type of ICacheManager such as SqlCacheManager or FileSystemCacheManager and switch between the implementations using a ConfigureableModule, something like this:

public class ConfigureableModule : IConfigurableModule
	{
		public void ConfigureContainer(ServiceConfigurationContext context)
		{
		    var container = context.Container;

		    container.Configure(c => c.For<ICacheManager>().Use<CacheManager>());
		}
	}

We could also have different concrete implementations for different environments by using profiles, like this:

public class ConfigureableModule : IConfigurableModule
 {
     public void ConfigureContainer(ServiceConfigurationContext context)
	{
		var container = context.Container;

		container.Configure(c => { 
			c.Profile("debug", ctx => { ctx.For<ICacheManager>().Use<NullCacheManager>(); 

			c.Profile("release", ctx => { ctx.For<ICacheManager>().Use<CacheManager>(); 
				}); 
			});  
		});
	}
 }
 

But wouldn’t it be pretty cool if we could switch the implementation at runtime? Turns out it’s not too difficult to achieve… Lets create an admin plugin where an administrator can choose which implementation of ICacheManager should currently be used by the site.

We start out by creating a simple plugin:

[EPiServer.PlugIn.GuiPlugIn(Area = EPiServer.PlugIn.PlugInArea.AdminConfigMenu, Url = "/modules/samplesite/ChangeCacheManager/Index", DisplayName = "Cache management")]
[Authorize(Roles = "Administrators")]
public class ChangeCacheManagerController : Controller
 {
	public ActionResult Index() { return View(); }
 }

Remember that we need to add this nonsense to the episerver.shell part of our web.config as well for our module to be picked up:

<episerver.shell>
    <publicModules rootPath="~/modules/" autoDiscovery="Minimal">
      <add name="samplesite">
        <assemblies>
          <add assembly="WhateverYouNameYourAssembly" />
        </assemblies>
      </add>
   </publicModules>
</episerver.shell>

Also please note that I’ve added an authorization attribute to the controller to make sure noone but an administrator stumbles upon it.

Add an empty index.cshtml view for now and type something awesome in it. Compile and run and make sure your plugin appears in the admin config menu.

Allright, next lets create a model for our view.

public class CacheManagerViewModel {

	public string SelectedManager { get; set; }

	public List<SelectListItem> ConfiguredManagers { get; set; }

	public CacheManagerViewModel() {
		ConfiguredManagers = new List<SelectListItem>();
	}
}

Simple, all our plugin will need is a list of the available cachemanagers and a string representing the currently selected one. Ok, lets flesh out our view next.

@model Web.AdminPlugins.Models.CacheManagerViewModel
<!DOCTYPE html>
<html>
    <head>
        <link rel="stylesheet" type="text/css" href="/EPiServer/Shell/7.11.1.0/ClientResources/epi/themes/legacy/ShellCore.css">
        <link rel="stylesheet" type="text/css" href="/EPiServer/Shell/7.11.1.0/ClientResources/epi/themes/legacy/ShellCoreLightTheme.css">
        <link href="../../../App_Themes/Default/Styles/ToolButton.css" type="text/css" rel="stylesheet">
        <title>Cache management</title>
    </head>
    <body>
        <div class="epi-contentContainer epi-padding">
            <h1>Select active cachemaanger</h1>
            <p>Select which CacheManager implementation to use for this site.</p> 

            @using (Html.BeginForm())
            {
                <div class="epi-padding">
                    <p>Currently active cachemanager: <strong>@Model.SelectedManager</strong></p>
                    <div class="epi-size25">                       
                        <div>

                            <label>Select cachemanager: </label>

                            @Html.DropDownListFor(m => m.SelectedManager, Model.ConfiguredManagers, new { @class = "episize240" })

                        </div>
                    </div>
                </div> 

                    <div class="epi-buttonContainer">

                    <span class="epi-cmsButton">

                        <input class="epi-cmsButton-text epi-cmsButton-tools epi-cmsButton-Save" type="submit" value="Save" />

                    </span>

                </div>
            }
        </div>
    </body>
</html>

Now we have a model and a view, all that’s left is to add some code to our controller:

public ActionResult Index()
{
    return View(GetViewModel());
}

[HttpPost]
public ActionResult Index(CacheManagerViewModel model)
{
    var container = ServiceLocator.Current.GetInstance<IContainer>(); 

    var selectedManager = Type.GetType(model.SelectedManager); 

    container.Model.EjectAndRemoveTypes(t => t == selectedManager);

    var instance = (ICacheManager)container.GetInstance(selectedManager);

    container.Configure(x => x.For<ICacheManager>().Use(instance));

    return View(GetViewModel());
} 

private CacheManagerViewModel GetViewModel()
{
    var model = new CacheManagerViewModel();

    var currentActiveManager = ServiceLocator.Current.GetInstance<ICacheManager>();

    model.SelectedManager = currentActiveManager.GetType().Name;

    foreach (var manager in ServiceLocator.Current.GetAllInstances<ICacheManager>())
    {
        model.ConfiguredManagers.Add(new SelectListItem()
       {
          Text = manager.GetType().Name,

          Value = manager.GetType().AssemblyQualifiedName,

          Selected = currentActiveManager.GetType() == manager.GetType()

        });
     }
     return model;
}

There are a few interesting points in the code above. Lets first consider GetViewModel() method. We simply get the currently active concrete implementation and then loop through all instances of ICacheManager that is currently registered in our IoC container. This brings us to an important point, allimplementations of ICacheManager must have been registered in our container or they wont be returned by the GetAllInstances method. This could be done in simple fashion by using the AddAllTypesOf<ICacheManager> when we configure our container. Something like this:

public void ConfigureContainer(ServiceConfigurationContext context) {

	context.Container.Configure(c => c.Scan(s => s.AddAllTypesOf<ICacheManager>()));
}

Next lets have a look at the post method. This method looks kinda peculiar, what’s all this ejecting and removing stuff? It turns out that we can’t really just change which configured cachemanager to use with For.Use because that would add another instance of the same type and not replace the existing one, and that’s why we first need to eject the model of the selected type and then re-configure it.

And that should be it! We now have a working admin plugin that lets us change fundamental inner workings of our application at runtime. We could substitute ICacheManager for IContentRepository and radically change how we fetch our content, we could substitute it for IContentRenderer and add functionality such a detailed logging to the rendering of our content… There’s no end to the possibilities!

But, as we’ve learned from modern classics such as the bible and the amazing spiderman:

License error