IoC in class library. Where to bootstrap

2019-01-21 13:50发布

问题:

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?

回答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.



回答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.



回答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.



回答4:

O.K Create a library named Project.Ioc. Install Unity here. Reference the other layers to Ioc library. And Ioc Library to presentation layer. Create a class named UnityHelper.

/// <summary>
/// Bind the given interface in request scope
/// </summary>
public static class IocExtensions
{
    public static void BindInRequestScope<T1, T2>(this IUnityContainer container) where T2 : T1
    {
        container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
    }

    public static void BindInSingletonScope<T1, T2>(this IUnityContainer container) where T2 : T1
    {
        container.RegisterType<T1, T2>(new ContainerControlledLifetimeManager());
    }
}

/// <summary>
/// The injection for Unity
/// </summary>
public static class UnityHelper
{

    public static IUnityContainer Start()
    {
        var container = BuildUnityContainer();

        DependencyResolver.SetResolver(new Unity.Mvc4.UnityDependencyResolver(container));

        return container;
    }

    /// <summary>
    /// Inject
    /// </summary>
    /// <returns></returns>
    private static IUnityContainer BuildUnityContainer()
    {
        var container = new UnityContainer();

        // register all your components with the container here
        // it is NOT necessary to register your controllers

        // Database context, one per request, ensure it is disposed
        container.BindInRequestScope<IMVCForumContext, MVCForumContext>();
        container.BindInRequestScope<IUnitOfWorkManager, UnitOfWorkManager>();

        //Bind the various domain model services and repositories that e.g. our controllers require         
        container.BindInRequestScope<ITopicService, TopicService>();

        container.BindInRequestScope<ITopicTagRepository, TopicTagRepository>();

        //container.BindInRequestScope<ISessionHelper, SessionHelper>();

        return container;
    }
}

Check out the MvcForum project. There is MvcForm.Ioc class library. The library gets other Layer references. There are only one class named UnityHelper.

https://github.com/leen3o/mvcforum

i hope this is wat you are looking for.