Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is a good strategy for initializing an IoC container within a class library?

I'm writing a class library (in C#) that will be distributed with applications I have no control over. My library is also a little bit security sensitive and thus I do not want to allow the calling app to do any configuration of the dependencies for the class library. It must be self-contained and initialize its own dependencies.

At the same time, I want it to be unit-testable and loosely coupled and I want to use an IoC container to manage the dependencies. At the moment I'm using internal constructors and [InternalsVisibleTo()] so that the unit tests can do manual injection. I prefer to use Ninject as my IoC container but that's sort of irrelevant to the question, I think.

I'm struggling to come up with a good strategy for initializing my IoC container in production, since the class library doesn't really have a defined entry point, it has a number of classes that can be instantiated with no way of knowing which one the app will use first.

I wondered if there may be some sort of AssemblyLoad event, and indeed the AppDomain seems to have such an event, but my assembly must already have been loaded into the AppDomain before I can even hook into that, so I would always miss the event raised by my own assembly being loaded. I also thought about using a static initializer or constructor, but I'm not really happy to pollute every possible class with IoC container setup because that makes them tightly coupled to the container. I don't want to decouple my code only to then couple it to the IoC container.

I have found a few other questions discussing this topic but none of them really deals with my situation. One suggested using a static initializer, another suggested that the application should always be the composition root.

Is there another way?

like image 425
Tim Long Avatar asked Dec 07 '25 09:12

Tim Long


2 Answers

There is a contradiction between your requirements.

First, you don't want the Composition Root because of the security concern. Second, you want dependencies to be resolved by a container. But since the library doesn't control the container, pretty much anything can be injected into your code, including stuff that would try to break anything from inside.

One approach would be to have your dependencies explicit so that library clients are responsible for feeding your dependencies.

namespace Library
{
    public class Foo1
    {
        //  a classical IoC dependendency
        public Foo1( IBar bar )
        {
        }
    }
}

On the other hand, using a Composition Root which is an explicit external point of initializing your library still doesn't mean your library is polluted. CR works great with a Local Factory (aka the Dependency Resolver) which is responsible for internal object creation and it is set up from within the CR.

namespace Library
{
    public interface IFoo { }

    // local Foo factory, with a customizable provider
    public class FooFactory
    {
        private static Func<IFoo> _provider;
        public static void SetProvider( Func<IFoo> provider )
        {
           _provider = provider;
        }

        public IFoo CreateFoo()
        {
            return _provider();
        }
    } 

    // Bar needs Foo
    public class Bar
    {
        public void Something()
        {
            // you can use the factory here safely
            // but the actual provider is configured elsewhere
            FooFactory factory = new FooFactory();

            IFoo foo = factory.CreateFoo();
        }
    }
}

and then somewhere in the Composition Root (close to application's entry point)

// kernel is set up to map IFoo to an implementation of your choice
public void ComposeRoot( IKernel kernel )
{
    FooFactory.SetProvider( () => kernel.Get<IFoo>() );
}

As you can see, instead of possibly providing multiple injection points in multiple classes, the Local Factory is a single injection point that provides a clean configuration of the whole library making it self-contained.

You could even have a provider that doesn't involve any IoC container but rather create a concrete implementation, thus making it easily testable without any container. Switching to another IoC is straightforward, you just provide another provider.

The larger your library is and your classes are cohesive (use each other often), the more convenient is to have a Local Factory. You don't need to rethrow dependencies between your classes (without a Local Factory, if your class A needs I and your B needs A then automatically B needs I), rather, all your classes depend on a single factory.

like image 71
Wiktor Zychla Avatar answered Dec 10 '25 01:12

Wiktor Zychla


There are two scenarios here:

A) Your consumers aren't using your Ninject container:

If you don't want them to be able to provide alternate configurations (or are injecting internal classes), you must create a constructor that will resolve these dependencies yourself. This will be your point of entry.

B) You consumers are using your Ninject container:

You will need to expose your Ninject kernel instance to your consumers. This can either be wrapped in a ServiceLocator if you want to hide the fact that you are using Ninject, or by simply exposing the kernel itself. In either case, you can make it a property on a static class which will be your entry point.

From an internal standpoint, I prefer using option B, but as a frequent consumer of 3rd party libraries, I have never seen anyone expose their IoC container, not would I ever want them to. I just want to be able to instantiate a class, and not worry about any internal implementation or dependencies. You milage may vary, of course.

like image 32
Troels Larsen Avatar answered Dec 10 '25 01:12

Troels Larsen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!