@Shazwazza

Shannon Deminick's blog all about web development

Easily setup your Umbraco installation with IoC / Dependency Injection

December 19, 2017 04:00
Easily setup your Umbraco installation with IoC / Dependency Injection

Umbraco supports allowing you to setup and configure any IoC container type that you want to use in your application. For a while now there’s been some sparse documentation on how to achieve this which you can find here: https://our.umbraco.org/Documentation/reference/using-ioc. As the Umbraco core codebase evolves, sometimes a new non-parameterless constructor is added to a class and sometimes this can confuse an existing container that you’ve setup. For many folks, fixing these errors after upgrading is a trial and error experience until they track down the dependency that is now missing from their container and finally add it.

Simone, a very helpful Umbracian, made a comment on the issue tracker and it’s something that is just so obvious  (http://issues.umbraco.org/issue/U4-9562#comment=67-41855):

I think the point here is:  as user of a framework, I shouldn't need to wire up dependencies for internals of the framework myself. I should only bother about my own dependencies.
Maybe Umbraco should ship a small extension method for each of the main IoC container out there which wires up all the internals.
Or come with a IoC container out of the box and then everyone using umbraco have to use that one.

Yes of course this should be done!

A new community project: Our.Umbraco.IoC

I decided to get the ball rolling with this one and have setup a new Git repo here: https://github.com/Shazwazza/Our.Umbraco.IoC 

Currently there are 2 different container configurations committed and working for Autofac and LightInject.

I’ve added some notes to the readme on how to contribute and get started so I’m hoping that some folks can create some Pull Requests to add support for more containers. The project is very easy to navigate, it’s got a build script and nuget packages setup.

Give it a go!

I’ve published some beta’s to Nuget:

Install-Package Our.Umbraco.IoC.Autofac
Install-Package Our.Umbraco.IoC.LightInject

You can actually install both and test each one independently by disabling them by an appSetting:

<add key="Our.Umbraco.IoC.Autofac.Enabled" value="false" />

Or

<add key="Our.Umbraco.IoC.LightInject.Enabled" value="false" />

If this config key doesn’t exist, it will assume the value is “true”

Using the container

Once you’ve got your desired package installed, it will be active in your solution (unless you disable it via config). At this stage you’ll want to add your own bits to the container, so here’s how you do that:

  • Create a custom Umbraco ApplicationEventHandler
  • Override ApplicationInitialized – we do this in this phase to bind to the container event before the container is built which occurs in the ApplicationStarted phase
  • Bind to the container event
  • add any custom services you want to the container

Here’s a full working example showing various techniques and includes the syntax for both LightInject and Autofac. In this example we’re registering a IServerInfoService as a request scoped object since it requires an HttpRequestBase. NOTE: That the basic web objects are already registered in the containers (such as HttpContextBase, HttpRequestBase, etc…)


public class MyUmbracoStartup : ApplicationEventHandler
{
    protected override void ApplicationInitialized(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
    {
        //If you are using Autofac:
        AutofacStartup.ContainerBuilding += (sender, args) =>
        {
            //add our own services
            args.Builder.RegisterControllers(typeof(TestController).Assembly);
            args.Builder.RegisterType().As().InstancePerRequest();
        };

        //If you are using LightInject:
        LightInjectStartup.ContainerBuilding += (sender, args) =>
        {
            //add our own services
            args.Container.RegisterControllers(typeof(TestController).Assembly);
            args.Container.Register(new PerRequestLifeTime());
        };
    }
}

//service
public interface IServerInfoService
{
    string GetValue();
}

//implementation of the service
public class ServerInfoService : IServerInfoService
{
    private readonly HttpRequestBase _umbCtx;

    //requires a request based object so this must be scoped to a request
    public ServerInfoService(HttpRequestBase umbCtx)
    {
        _umbCtx = umbCtx;
    }

    public string GetValue()
    {
        var sb = new StringBuilder();
        sb.AppendLine("Server info!").AppendLine();
        foreach (var key in _umbCtx.ServerVariables.AllKeys)
        {
            sb.AppendLine($"{key} = {_umbCtx.ServerVariables[key]}");
        }
        return sb.ToString();
    }
}

public class TestController : SurfaceController
{
    private readonly IServerInfoService _serverInfoService;

    public TestController(IServerInfoService serverInfoService, UmbracoContext umbCtx): base(umbCtx)
    {
        _serverInfoService = serverInfoService;
    }

    //see /umbraco/surface/test/index to see the result
    public ActionResult Index()
    {
        return Content(_serverInfoService.GetValue(), "text/plain");
    }
}

Happy holidays!

Using IoC with Umbraco & MVC

October 31, 2012 18:08

The question was asked on my post yesterday about the upcoming Umbraco 4.10.0 release with MVC support and whether it is possible to use IoC/Dependency Injection with our implementation. The answer is definitely yes!

One of the decisions we’ve made for the code of Umbraco is to not use IoC in the core. This is not because we don’t like IoC (in fact I absolutely love it) but more because things start to get really messy when not 100% of your developers understand it and use it properly. Since Umbraco is open source there are developers from many walks of life committing code to the core and we’d rather not force a programming pattern on them. Additionally, if some developers don’t fully grasp this pattern this leads to strange and inconsistent code and even worse if developers don’t understand this pattern then sometimes IoC can be very difficult to debug.

This ultimately means things are better for you since we won’t get in the way with whatever IoC framework you choose.

Which frameworks can i use?

Theoretically you can use whatever IoC framework that you’d like, though I haven’t tested or even tried most of them I’m assuming if they are reasonable frameworks that work with MVC then you should be fine. I’m an Autofac fan and to be honest I’ve only dabbled in other frameworks so all examples in this post and documentation are based on Autofac. Since we don’t use any IoC, it also means that we are not specifying a DependencyResolver so you are free to set this to whatever you like (I’d assume that most IoC frameworks would integrate with MVC via the DependencyResolver).

How do i do it?

I chucked up some docs on github here which I’ll basically just reiterate on this post again with some more points. Assuming that you construct your IoC container in your global.asax, the first thing you’ll have to do is to create this class and make sure it inherits from the Umbraco one (otherwise nothing will work). Then just override OnApplicationStarted and build up your container. Here’s an example (using Autofac):

/// <summary>
/// The global.asax class
/// </summary>
public class MyApplication : Umbraco.Web.UmbracoApplication
{
    protected override void OnApplicationStarted(object sender, EventArgs e)
    {
        base.OnApplicationStarted(sender, e);

        var builder = new ContainerBuilder();

        //register all controllers found in this assembly
        builder.RegisterControllers(typeof(MyApplication).Assembly);

        //add custom class to the container as Transient instance
        builder.RegisterType<MyAwesomeContext>();

        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Notice that I’ve also registered a custom class called MyAwesomeContext in to my container, this is just to show you that IoC is working. Of course you can do whatever you like with your own container :) Here’s the class:

public class MyAwesomeContext
{
    public MyAwesomeContext()
    {
        MyId = Guid.NewGuid();
    }
    public Guid MyId { get; private set; }
}

Next we’ll whip up a custom controller to hijack all routes for any content item that is of a Document Type called ‘Home’ (there’s documentation on github about hijacking routes too):

public class HomeController : RenderMvcController
{
    private readonly MyAwesomeContext _myAwesome;

    public HomeController(MyAwesomeContext myAwesome)
    {
        _myAwesome = myAwesome;
    }

    public override ActionResult Index(Umbraco.Web.Models.RenderModel model)
    {
        //get the current template name
        var template = this.ControllerContext.RouteData.Values["action"].ToString();
        //return the view with the model as the id of the custom class
        return View(template, _myAwesome.MyId);
    }
}

In the above controller, a new instance of MyAwesomeContext will be injected into the constructor, in the Index action we’re going to return the view that matches the currently routed template and set the model of the view to the id of the custom MyAwesomeContext object (This is just an example, you’d probably do something much more useful than this).

We can also do something similar with SurfaceControllers (or any controller you like):

public class MyTestSurfaceController : SurfaceController
{
    private readonly MyAwesomeContext _myAwesome;

    public MyTestSurfaceController(MyAwesomeContext myAwesome)
    {
        _myAwesome = myAwesome;
    }

    [ChildActionOnly]
    public ActionResult HelloWorld()
    {
        return Content("Hello World! Here is my id " + _myAwesome.MyId);
    }
}

That’s it?

Yup, these are just examples of creating controllers with IoC, the actual IoC setup is super easy and should pretty much work out of the box with whatever IoC framework you choose. However, you should probably read the ‘Things to note’ in the documentation in case your IoC engine of choice does something wacky with the controller factory.

Using metadata with MEF in Medium Trust

March 3, 2011 19:20

One of the awesome parts of MEF is that you can attach metadata to your objects. This allows you to use the new Lazy<T, TMetadata> object so you can inspect the meta data about an object without instantiating it. In order to attach metadata to an object, you need (should have) some entity that contains properties to support the meta data such as an interface (good tutorial site here ). For example, if you have a class of type MyObjectType and an interface to store metadata about the class of type IMyMetadata, you can then resolve/import Lazy<MyObjectType, IMyMetadata> which will expose a Metadata property of type IMyMetadata with the information filled in. You might already be able to tell that a bit of magic is happening behind the scenes since there is no concrete class for IMyMetadata but MEF has magically found a way to instantiate a class of type IMyMetadata and fill in the values. This is the problem area if you are running in Medium Trust, you will get a SecurityException stemming form the System.ComponentModel.Composition.MetadataViewGenerator. This same issue will occur when using Autofac to register metadata with the .WithMetadata extension method.

Solution

To work around this problem in Medium Trust, you’ll need to create a concrete class to store your metadata in instead of just referencing an interface. You’ll also need to ensure that the constructor of this class takes in one parameter of type IDictionary<string, object> and using this object you’ll need to manually do the heavy lifting of assigning the values to the properties of this class based on the values in the dictionary. Better yet, you can just make a base class that all your metadata classes can inherit from. In Umbraco v5, we’ve called this abstract class MetadataComposition just to keep inline with the naming conventions of the MEF namespace:

public abstract class MetadataComposition { /// <summary> /// constructor, sets all properties of this object based /// on the dictionary values /// </summary> /// <param name="obj"></param> protected MetadataComposition(IDictionary<string, object> obj) { var t = GetType(); var props = t.GetProperties(); foreach (var a in obj) { var p = props.Where(x => x.Name == a.Key).SingleOrDefault(); if (p != null) { p.SetValue(this, a.Value, null); } } } }

Hope this helps someone!

MVC Controllers as plugins with MEF and Autofac

December 1, 2010 01:38

Disclaimer

This blog posts talks about Umbraco v5 (Jupiter), however since the state of the codebase is in it’s infancy, the details about Umbraco v5 in this article may not be accurate once Umbraco v5 is released.

Plugins

In Umbraco v5 we’re introducing the concept of plugins. Currently in Umbraco, we have the notion of plugins such as Trees, Actions (used on context menus), Data types, etc… Each of these objects is found using a class called TypeFinder to search for specific types in all assemblies that are loaded in the ‘bin’ folder. Though this works, it is definitely not the most efficient approach to plugins. For Umbraco v5, we’re now using MEF as the plugin framework which is now part of .Net framework 4.  There will be many coming blog posts about the different types of plugins, how they work and how to create them using MEF, but this blog post is about overcoming a small hurdle we encountered when trying to integrate MEF, Autofac and MVC controllers as 3rd party plugins….

MVC Controllers as plugins

For Umbraco v5, we’re using Autofac as the IoC container which works really well for injecting dependencies into MVC controllers (amongst a ton of other things). Autofac also plays really nicely with MEF through it’s MEF integration library and requires all of 2 lines of code to get the MEF plugins into our IoC container:

var catalog = new DirectoryCatalog( m_HttpServer.MapPath("~/Plugins/Trees")); builder.RegisterComposablePartCatalog(catalog);

The above code tells Autofac to register all exportable MEF components that are found in all assemblies in the ~/Plugins/Trees folder. In our case, the plugins in this folder are actually MVC Controllers which will get registered in to the IoC container and since we are currently using the AutofacControllerFactory for our MVC controller factory, it ‘should’ be able to create the requested controller and inject it’s dependencies because we’ve stuck it in the container.

Problem 1

So far this is all very easy, but as soon as we want to route a request to this controller we end up with a 404 error stating that the page/controller cannot be found.  The reason for this is because the AutofacControllerFactory inherits from the DefaultControllerFactory which searches for controllers using the BuildManager which only searches for types registered in the ‘bin’ folder. I was hoping that the AutofacControllerFactory would have also searched within it’s registrations but this turns out to not be the case. (In fact, once Autofac releases it’s MVC3 implementation, it will no longer be shipped with an AutofacControllerFactory and instead be using MVC3’s new DependencyResolver).

Solution 1

In order for this to work we have to create our own ControllerFactory  (source code at the end of this article) and provide our own implementation for the methods:

IController GetControllerInstance(RequestContext context, Type controllerType) Type GetControllerType(RequestContext requestContext, string controllerName)

The GetControllerType implementation does the following:

  • Checks if the requested controllerName has already been resolved and exists in our factory’s internal cache
  • If not, try to find the controller by name from the underlying DefaultControllerFactory
  • If not found, search the IoC container’s registrations for the type,
    • if found, store the reference Type in the factory’s internal cache

The GetControllerInstance does the following:

  • Check if the controllerType has been registered in the factory’s internal cache
    • If so, get the return the resolved IController instance from the container
  • If not, try to create the controller from the underlying DefaultControllerFactory

Problem 2

The next problem we encountered was that Autofac couldn’t inject dependent objects into the controllers that weren’t registered in the container using the Autofac MEF extensions. A quick post on to the Autofac Google Group and I had my answer (read the post if you want examples)!

Solution 2

Essentially, if you want to expose objects to MEF in Autofac, you have to explicitly register them in the container with the ‘Exported’ extension method. Example:

//register the umbraco settings builder.Register<UmbracoSettings>(x => UmbracoSettings.GetSettings()) .As<IUmbracoSettings>() //ensure it's registered for MEF too .Exported(x => x.As<IUmbracoSettings>()) //only have one instance ever .SingleInstance();

Problem 3

The last problem was that by default it seems that exported MEF components are instantiated as singletons, meaning that each time you try to resolve a MEF component of a certain type it will always be the exact same instance. MVC really doesn’t like this when it comes to controllers, in fact MVC gives you a very informative error message regarding this and explains that if you are using dependency injection to create controllers to make sure the container is configured to resolve new controller objects, not the same instance.

Solution 3

All we need to do is add a PartCreationPolicyAttribute to the exported MEF type and set it to CreationPolicy.NonShared which tells the framework to create a new instance each time it is resolved. Example:

[Tree(typeof(ContentTreeController))] [PartCreationPolicy(CreationPolicy.NonShared)] public class ContentTreeController : DemoDataTreeController

UmbracoControllerFactory source

Here’s the source code for the custom controller factory, it currently inherits from AutofacControllerFactory but once we upgrade to use the new MVC 3 Autofac implementation, this will simply inherit from DefaultControllerFactory.

using System; using System.Web; using System.Web.Mvc; using System.Linq; using System.Web.Routing; using Autofac; using Autofac.Core; using Autofac.Integration.Web; using Autofac.Integration.Web.Mvc; using System.Collections.Generic; using Autofac.Features.Metadata; using UmbracoProjects.CMS.Web.Trees.Controllers; using System.Text.RegularExpressions; namespace UmbracoProjects.CMS.Web.Mvc.Controllers { public class UmbracoControllerFactory : AutofacControllerFactory { protected IContainer m_Container; private static readonly object m_Locker = new object(); /// <summary> /// Used to cache found controllers in the IoC container /// </summary> private static readonly Dictionary<string, ServiceTypeCache> m_IoCControllerCache = new Dictionary<string, ServiceTypeCache>(); private class ServiceTypeCache { public Service Service { get; set; } public Type ComponentType { get; set; } } /// <summary> /// Initializes a new instance of the <see cref="AutofacControllerFactory"/> class. /// </summary> /// <param name="containerProvider">The container provider.</param> public UmbracoControllerFactory(IContainerProvider containerProvider) : base(containerProvider) { m_Container = containerProvider.ApplicationContainer; } /// <summary> /// Creates the controller based on controller type /// </summary> /// <param name="context">The context.</param> /// <param name="controllerType">Type of the controller.</param> /// <returns>The controller.</returns> /// <remarks> /// This first checks our IoC service internal cache to see if it exists, if it does, we'll try to resolve the controller /// from IoC, otherwise we'll try to resolve the controller from the underlying factory /// </remarks> protected override IController GetControllerInstance(RequestContext context, Type controllerType) { if (context == null) throw new ArgumentNullException("context"); //first, check if this service by this type is in our cache if (m_IoCControllerCache.Where(x => x.Value.ComponentType.Equals(controllerType)).Any()) { var controllerService = m_IoCControllerCache.Where(x => x.Value.ComponentType.Equals(controllerType)).SingleOrDefault().Value.Service; object controller; if (m_Container.TryResolveService(controllerService, out controller)) { //if the controller is created by MEF, then resolve if (controller is System.ComponentModel.Composition.Primitives.Export) { return (IController)((System.ComponentModel.Composition.Primitives.Export)controller).Value; } else if (controller is IController) { return (IController)controller; } } throw new HttpException(404, string.Format("Controller type " + controllerType + " could not be resolved", controllerService, controllerType.FullName, context.HttpContext.Request.Path)); } //otherwise, try to create from the underlying factory return base.GetControllerInstance(context, controllerType); } /// <summary> /// Finds a controller type based on it's name. /// </summary> /// <param name="requestContext"></param> /// <param name="controllerName"></param> /// <returns></returns> /// <remarks> /// This searches using the DefaultControllerFactory's implementation and /// also searches the IoC container for registered types since types that are registered in the container /// may exist outside the bin folder /// </remarks> protected override Type GetControllerType(RequestContext requestContext, string controllerName) { var controllerKeyName = controllerName.ToUpper(); //first, check if this is already in our internal cache if (m_IoCControllerCache.ContainsKey(controllerKeyName)) { return m_IoCControllerCache[controllerKeyName].ComponentType; } //next, try to resolve it from the underlying factory var type = base.GetControllerType(requestContext, controllerName); //if the controller can't be resolved by name using the standard method, then we should check our container if (type == null) { //we need to search the registered components in IoC for a match foreach(var reg in m_Container.ComponentRegistry.Registrations) { foreach (var s in reg.Services) { //match namespaces/classe that then end with "controllerName + Controller" if (Regex.IsMatch(s.Description, @"(?:\w|\.)+\." + controllerName + "Controller$", RegexOptions.Compiled | RegexOptions.IgnoreCase)) { var controller = m_Container.ResolveService(s); var cType = controller.GetType(); //add to the internal cache if (!m_IoCControllerCache.ContainsKey(controllerKeyName)) { lock (m_Locker) { //double check if (!m_IoCControllerCache.ContainsKey(controllerKeyName)) { m_IoCControllerCache.Add(controllerKeyName, new ServiceTypeCache() { Service = s, ComponentType = cType }); } } } return cType; } } } } return type; } } }