IoC in class library. Where to bootstrap

22,856

Solution 1

This is an interesting problem. How can you dependency inject re-usable assemblies that do not have an entry point. I would really like to see other people's answer.

Dependency injection is the responsibility of the entry-point assembly. Yet, if you have a lot of classes and assemblies each needing DI, then it is possible that they are left-off for some classes/assemblies and the task becomes onerous.

Solution One

Using convention over configuration. You stick to a rule of class Foo implementing IFoo, etc. A lot of DI frameworks have the means to set it up using convention.

Solution two

Above solution does not solve all problems since sometimes you need to parameterise the setup of the injection. Here is how I have solved the problem (especially for those assemblies loaded by MEF and this is for AutoFac):

Created an interface IIocInstaller where container (or builder is passed)

public interface IIocInstaller
{
    void Setup(ContainerBuilder builder);
}

Created an assembly attribute that flags assemblies needing DI:

[AttributeUsage(AttributeTargets.Assembly)]
public class ExportAssemblyAttribute : Attribute
{
}

In each assembly, I create a class that sets up DI:

[assembly: ExportAssembly]
namespace This.That
{

    [Export(typeof(IIocInstaller))]
    public class IocInstaller : IIocInstaller
    {
        public void Setup(ContainerBuilder builder)
        {
            ....
        }
    }
}

Then in the entry point, I have a common code which looks through all loaded assemblies (including MEFed ones) that have the assembly attribute and then look for the type implementing the IIocInstaller and then call Setup on them.

Solution 2

I know that an answer has been chosen, however I think a part of Unity is being overlooked. Since this was a specific Unity question I thought I point out the UnityContainerExtension base class that implements the IUnityContainerExtensionConfigurator. This is there for API library to extend to make it easy for the entry-point application who owns the Container to have an easy way to make sure your library gets registered with the Container correctly, and allows the API owner control of what gets registered and how.

This is used by the Microsoft Enterprise Libraries for this purpose.

I am going to use a Logging library as a simple:

public class LoggingUnityExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Container.RegisterType<ILogger, Logger>(new ContainerControlledLifetimeManager());
    }
}

Then the entry-point application does this:

public class Bootstrapper : UnityBootstrapper
{
    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();

        Container.AddNewExtension<EnterpriseLibraryCoreExtension>();
        Container.AddNewExtension<LoggingUnityExtension>();
        // ... 
    }

    // ...
}

Now they have registered Enterprise Library and the API for the Logging library. It's very simple for the entry-point application with this method which is what any library developer should have as a goal.

Solution 3

Doing it from the calling application puts more burden on the calling application. Leaving the chance to omit the initialization and get into trouble.

I would do it in the class library, for example in a static constructor.

Share:
22,856
Patrick
Author by

Patrick

Updated on September 18, 2020

Comments

  • Patrick
    Patrick over 3 years

    I'm using a class library that can be reused by other components. In this class library I'm using unity for dependency injection. For this class library I create a test project. The caller also gets a test project. One thing I'm uncertain about is the location of the bindings. Should I incorporate this in the class library or should I do this from the calling application?