@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!

Custom Assembly loading with Asp.Net Core

March 15, 2016 14:58

Building a plugin system in Asp.Net Core is a dream compared to previous Asp.Net versions!

In previous versions it was not really feasible to load Assemblies located outside of the /bin folder for a web application. I battled with this concept quite a long time ago and although it’s sort of possible, the notion of having a plugin system that supported loading DLLs from outside of the /bin folder was riddled with hacks/problems and not really supported OOTB.

A large part of the issues has to do with something called an ‘Assembly Load Context’. In traditional .Net there are 3 of these context types: “Load”, “LoadFrom” and “Neither”, here’s a very old but very relevant post about these contexts from Suzanne Cook. In traditional Asp.Net, the “Load” context is used as the default context and it is managed by something called Fusion (.Net’s normal Assembly Loader/Binder). The problem with this context is that it is difficult to load an assembly into it that isn’t located in Fusion’s probing paths (i.e. /bin folder). If you load in an Assembly with a different Assembly Load Context and then try to mix it’s Types with the Types from the default context  … you’ll quickly see that it’s not going to work.

The “Neither” context

Here is the Neither context definition as defined by Suzanne Cook:

If the user generated or found the assembly instead of Fusion, it's in neither context. This applies to assemblies loaded by Assembly.Load(byte[]) and Reflection Emit assemblies (that haven't been loaded from disk). Assembly.LoadFile() assemblies are also generally loaded into this context, even though a path is given (because it doesn't go through Fusion).

In Asp.Net Core (targeting CoreCLR), the default Assembly Load Context is the “Neither” context. This is a flexible context because it doesn’t use Fusion and  it allows for loading assemblies any way that you want - including loading an assembly from a byte array, from a path or by a name. Since all of Asp.Net Core uses this context it means that all of the types loaded in with this context can talk to each other without having the previous Asp.Net problems.

I would assume that Asp.Net Core targeting Desktop CLR would still operate the same as before and still have the 3 types of Assembly Load Context’s … Maybe someone over at Microsoft can elaborate on that one? (David Fowler… surely you know? :)

Finding referenced plugin assemblies

In many cases if you create a product that supports plugin types, developers will create plugins for your product and ship them via Nuget. This is a pretty standard approach since it allows developers that are using your product to install plugins from the Nuget command line or from within Visual Studio. In this case plugin types will be found in referenced assemblies to your application and will be automatically loaded. Asp.Net Core has an interface called Microsoft.Extensions.PlatformAbstractions.ILibraryManager that can be used to resolve your application’s currently referenced ‘Libraries’ (i.e Nuget packages) and then each ‘Library’ returned exposes the Assemblies that it includes. Asp.Net MVC 6 has an even more helpful interface called Microsoft.AspNet.Mvc.Infrastructure.IAssemblyProvider which returns a list of referenced assemblies that are filtered based on if they are assemblies that reference a subset of MVC assemblies. The default implementation of IAssemblyProvider (DefaultAssemblyProvider) is extensible and we can use it to override it’s property ReferenceAssemblies in order to supply our own product assembly names instead of the MVC ones. This is perfect since this allows us to get a list of candidate assemblies that might contain plugins for your product:

public class ReferencePluginAssemblyProvider : DefaultAssemblyProvider
{
    //NOTE: The DefaultAssemblyProvider uses ILibraryManager to do the library/assembly querying
    public ReferencePluginAssemblyProvider(ILibraryManager libraryManager) : base(libraryManager)
    {
    }

    protected override HashSet<string> ReferenceAssemblies 
        => new HashSet<string>(new[] {"MyProduct.Web", "MyProduct.Core"});
}

now if you want to get a list of candidate assemblies that your application is referencing you could do:

//returns all assemblies that reference your product Assemblies
var candidateReferenceAssemblies = referencedPluginAssemblyProvider.CandidateAssemblies;

Finding and loading non-referenced plugin assemblies

This is where things get fun since this is the type of thing that wasn’t really very feasible with traditional Asp.Net web apps. Lets say you have a plugin framework where a plugin is installed via your web app, not in Visual Studio and therefore not directly referenced in your project. For this example, the plugin is a self contained collection of files and folders which could consist of: Css, JavaScript, Razor Views, and Assemblies. This plugin model is pretty nice since to install the plugin would mean just dropping the plugin folder into the right directory in your app and similarly  to uninstall it you can just remove the folder.  The first step is to be able to load in these plugin Assemblies from custom locations. For an example, let’s assume the web app has the following folder structure:

  • App Root
    • App_Plugins <—This will be the directory that contains plugin folders
      • MyPlugin1
        • bin <—by convention we’ll search for Assemblies in the /bin folder inside of a plugin
        • Views
      • MyPlugin2
        • bin <—by convention we’ll search for Assemblies in the /bin folder inside of a plugin
        • css
    • Views
    • wwwroot

IAssemblyLoader

The first thing we need is an ‘Microsoft.Extensions.PlatformAbstractions.IAssemblyLoader’, this is the thing that will do the assembly loading into the Assembly Load Context based on an AssemblyName and a location of a DLL:

public class DirectoryLoader : IAssemblyLoader
{
    private readonly IAssemblyLoadContext _context;
    private readonly DirectoryInfo _path;

    public DirectoryLoader(DirectoryInfo path, IAssemblyLoadContext context)
    {
        _path = path;
        _context = context;
    }

    public Assembly Load(AssemblyName assemblyName)
    {
        return _context.LoadFile(Path.Combine(_path.FullName, assemblyName.Name + ".dll"));
    }

    public IntPtr LoadUnmanagedLibrary(string name)
    {
        //this isn't going to load any unmanaged libraries, just throw
        throw new NotImplementedException();
    }
}

IAssemblyProvider

Next up we’ll need a custom IAssemblyProvider but instead of using the one MVC ships with, this one will be totally custom in order to load and resolve the assemblies based on the plugin’s /bin folders. The following code should be pretty straight forward, the CandidateAssemblies property iterates over each found /bin folder inside of a plugin’s folder inside of App_Plugins. For each /bin folder found it creates a DirectoryLoader mentioned above and loads in each DLL found by it’s AssemblyName into the current Assembly Load Context.

/// <summary>
/// This will return assemblies found in App_Plugins plugin's /bin folders
/// </summary>
public class CustomDirectoryAssemblyProvider : IAssemblyProvider
{
    private readonly IFileProvider _fileProvider;
    private readonly IAssemblyLoadContextAccessor _loadContextAccessor;
    private readonly IAssemblyLoaderContainer _assemblyLoaderContainer;

    public CustomDirectoryAssemblyProvider(
            IFileProvider fileProvider, 
            IAssemblyLoadContextAccessor loadContextAccessor, 
            IAssemblyLoaderContainer assemblyLoaderContainer)
    {
        _fileProvider = fileProvider;
        _loadContextAccessor = loadContextAccessor;
        _assemblyLoaderContainer = assemblyLoaderContainer;
    }

    public IEnumerable<Assembly> CandidateAssemblies
    {
        get
        {
            var content = _fileProvider.GetDirectoryContents("/App_Plugins");
            if (!content.Exists) yield break;
            foreach (var pluginDir in content.Where(x => x.IsDirectory))
            {
                var binDir = new DirectoryInfo(Path.Combine(pluginDir.PhysicalPath, "bin"));
                if (!binDir.Exists) continue;
                foreach (var assembly in GetAssembliesInFolder(binDir))
                {
                    yield return assembly;
                }
            }
        }
    }

    /// <summary>
    /// Returns assemblies loaded from /bin folders inside of App_Plugins
    /// </summary>
    /// <param name="binPath"></param>
    /// <returns></returns>
    private IEnumerable<Assembly> GetAssembliesInFolder(DirectoryInfo binPath)
    {
        // Use the default load context
        var loadContext = _loadContextAccessor.Default;

        // Add the loader to the container so that any call to Assembly.Load 
        // will call the load context back (if it's not already loaded)
        using (_assemblyLoaderContainer.AddLoader(
            new DirectoryLoader(binPath, loadContext)))
        {
            foreach (var fileSystemInfo in binPath.GetFileSystemInfos("*.dll"))
            {
                //// In theory you should be able to use Assembly.Load() here instead
                //var assembly1 = Assembly.Load(AssemblyName.GetAssemblyName(fileSystemInfo.FullName));
                var assembly2 = loadContext.Load(AssemblyName.GetAssemblyName(fileSystemInfo.FullName));
                yield return assembly2;
            }
        }
    }
}

That’s pretty much it! If you have an instance of CustomDirectoryAssemblyProvider then you can get Assembly references to all of the assemblies found in App_Plugins:

//returns all plugin assemblies found in App_Plugins
var candidatePluginAssemblies = customDirectoryAssemblyProvider.CandidateAssemblies;

Integrating non-referenced plugins/Assemblies with MVC

What if you had custom plugin types as MVC Controllers or other MVC types? By default MVC only knows about assemblies that your project has references to based on the DefaultAssemblyLoader.  If we wanted MVC to know about Controllers that exist in a plugin not referenced by your project (i.e. in App_Plugins) then it’s a case of registering a custom IAssemblyProvider in IoC which will get resolved by MVC. To make this super flexible we can create a custom IAssemblyProvider that wraps multiple other ones and allows you to pass in a custom referenceAssemblies filter if you wanted to use this to resolve your own plugin types:

public class CompositeAssemblyProvider : DefaultAssemblyProvider
{
    private readonly IAssemblyProvider[] _additionalProviders;
    private readonly string[] _referenceAssemblies;

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="libraryManager"></param>
    /// <param name="additionalProviders">
    /// If passed in will concat the assemblies returned from these 
    /// providers with the default assemblies referenced
    /// </param>
    /// <param name="referenceAssemblies">
    /// If passed in it will filter the candidate libraries to ones
    /// that reference the assembly names passed in. 
    /// (i.e. "MyProduct.Web", "MyProduct.Core" )
    /// </param>
    public CompositeAssemblyProvider(
        ILibraryManager libraryManager,
        IAssemblyProvider[] additionalProviders = null,
        string[] referenceAssemblies = null) : base(libraryManager)
    {
        _additionalProviders = additionalProviders;
        _referenceAssemblies = referenceAssemblies;
    }

    /// <summary>
    /// Uses the default filter if a custom list of reference
    /// assemblies has not been provided
    /// </summary>
    protected override HashSet<string> ReferenceAssemblies
        => _referenceAssemblies == null
            ? base.ReferenceAssemblies
            : new HashSet<string>(_referenceAssemblies);
    
    /// <summary>
    /// Returns the base Libraries referenced along with any DLLs/Libraries
    /// returned from the custom IAssemblyProvider passed in
    /// </summary>
    /// <returns></returns>
    protected override IEnumerable<Library> GetCandidateLibraries()
    {
        var baseCandidates = base.GetCandidateLibraries();
        if (_additionalProviders == null) return baseCandidates;
        return baseCandidates               
            .Concat(
            _additionalProviders.SelectMany(provider => provider.CandidateAssemblies.Select(
                x => new Library(x.FullName, null, Path.GetDirectoryName(x.Location), null, Enumerable.Empty<string>(),
                    new[] { new AssemblyName(x.FullName) }))));
    }
}

To register this in IoC you just need to make sure it’s registered after you register MVC so that it overrides the last registered IAssemblyProvider:

//Add MVC services
services.AddMvc();
//Replace the default IAssemblyProvider with the composite one
services.AddSingleton<IAssemblyProvider, CompositeAssemblyProvider>(provider =>
{
    //create the custom plugin directory provider
    var hosting = provider.GetRequiredService<IApplicationEnvironment>();
    var fileProvider = new PhysicalFileProvider(hosting.ApplicationBasePath);
    var pluginAssemblyProvider = new CustomDirectoryAssemblyProvider(
        fileProvider,         
        PlatformServices.Default.AssemblyLoadContextAccessor,
        PlatformServices.Default.AssemblyLoaderContainer);
    //return the composite one - this wraps the default MVC one
    return new CompositeAssemblyProvider(
        provider.GetRequiredService<ILibraryManager>(),
        new IAssemblyProvider[] {pluginAssemblyProvider});
});

 

Your all set! Now you have the ability to load in Assemblies from any location you want, you could even load them in as byte array’s from an external data source.  What’s great about all of this is that it just works and you can integrate these external Assemblies into MVC.

Some things worth noting:

  • Parts of the assembly loading APIs are changing a bit in Asp.Net Core RC2: https://github.com/aspnet/Announcements/issues/149
  • The above code doesn’t take into account what happens if you load in the same Assembly from multiple locations. In this case, the last one in wins/is active AFAIK – I haven’t tested this yet but I’m pretty sure that’s how it works.
  • You may have some issues if load in the same Assembly more than once from multiple locations if those Assemblies have different strong names, or major versions applied to them – I also haven’t tested this yet

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.

Registering custom components in IoC for Umbraco 5 plugins

January 2, 2012 02:23

Sometimes you might need to add some of your own components to the IoC container in Umbraco 5 for your plugins to function. We’ve made this extremely easy to do and it only requires 2 steps:

Create a custom class that implements Umbraco.Framework.DependencyManagement.IDependencyDemandBuilder . Ensure that this class does not have any constructor parameters otherwise it will not work. There’s only one method to implement and you can use the containerBuilder parameter to add stuff to the IoC container:

void Build(IContainerBuilder containerBuilder, IBuilderContext context);


Next you need to attribute your plugin (i.e. Tree, Editor, Menu Item, Property Editor, Surface Controller, etc….) to tell it which ‘Demand Builder’ to use. Easiest to explain with an example:

[DemandsDependencies(typeof(MyCustomBuilder))]
[Tree("4883C970-2499-488E-A963-5204F6C6F840", "My Tree")]
public class MyCustomTree : TreeController

The above code will ensure that the ‘Demand Builder’ of type MyCustomBuilder will be executed when this plugin is loaded

Thats it! Now you can add anything you need to the IoC container if you require this for your plugin.