31

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?

M.Babcock
  • 18,753
  • 6
  • 54
  • 84
Patrick
  • 2,730
  • 4
  • 33
  • 55
  • 1
    You can have a look at [p&p's Enterprise Library](http://entlib.codeplex.com). They are using Unity under the covers and the container is initialized without user interaction. – Sebastian Weber Apr 17 '12 at 12:41
  • @Sebastian, that is not entirely true the entry-point application (aka the user) has to initialize a container and register the enterprise library unity extension. See my answer for an example of this below. – Rodney S. Foley Sep 04 '12 at 16:50

4 Answers4

27

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.

Rodney S. Foley
  • 10,190
  • 12
  • 48
  • 66
  • 1
    Might want to reference the library so people can use your example easily. Do you need the `EnterpriseLibraryCoreExtension` for this to work? – ajbeaven May 10 '13 at 07:23
  • That depends on your needs, but typically when you use Unity and any of the EntLib Blocks you will need to use it almost always. Remember these are code snippets to show examples not fully functional code. I do state in my comments that I am using Microsoft Enterprise Libraries that should be sufficient for the example. As for the ILogger and Logger those are used only to show how to use a container extension not to show you how to create a Logger. That implementation is not relevant. – Rodney S. Foley May 12 '13 at 06:26
8

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.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • Thanks for the suggestion. It got me out of my thinking pattern. We are now introducing a seperate setup project. In this way the class library can use different implementations. – Patrick Apr 18 '12 at 09:11
  • @Patrick it is a difficult problem. Static constructors do not necessarily work since it might be late for DI, especially for two step containers such as AutoFac. – Aliostad Apr 18 '12 at 09:12
  • First this is an Autofac howto response to a Unity question. Also having a library support multiple IoC/DI containers shouldn't be to hard if the containers support the concept extensions in a way similar to Unity. You would just have a dedicated extensions class that uses the container of the IoC/DI implementer and then the entry-point application can pick the extension that matches their choice of IoC/DI. – Rodney S. Foley Sep 04 '12 at 16:58
2

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.

Ventsyslav Raikov
  • 6,882
  • 1
  • 25
  • 28
  • 3
    That is not a wise choice. The entry-point application should own and control the container, which is why unity allows for registering extensions and the UnityContainerExtension base class to make it easy to implement the IUnityContainerExtensionConfigurator interface. See my answer for an example. – Rodney S. Foley Sep 04 '12 at 16:52
  • This is a simple approach that would work in the right environment. – Richard Moore Oct 27 '18 at 17:56
0

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.