Matt
Matt

Reputation: 6963

Generic DI Container wrapper and use with common framework and app

I am building a framework from the ground-up (in .NET) to be used by my company and I want to make dependency injection core to this solution. So, the framework needs to make use of DI and the apps do too. Easy enough, but what I really want to do is to NOT have the consumers of this framework tied into using any 1 particular DI Container. I want them to be free to choose Autofac, StructureMap, Ninject or whatever else they want. I can define an interface for dependency resolution like so:

public interface IDIContainerAdapter
    {
        T Resolve<T>();

        object Resolve(Type type);

        T TryResolve<T>();

        object TryResolve(Type type);
    }

In theory you could then have something like this:

public class AutofacDIContainerAdapter : IDIContainerAdapter
    {
        private readonly IContainer container;

        public AutofacDIContainerAdapter(IContainer container)
        {
            this.container = container;
        }

        #region IDIContainerAdapter Members

        public T Resolve<T>()
        {
            return container.Resolve<T>();
        }

        public object Resolve(Type type)
        {
            return container.Resolve(type);
        }

        public T TryResolve<T>()
        {
            T result;

            if (container.TryResolve<T>(out result))
            {
                return result;
            }

            return default(T);
        }

        public object TryResolve(Type type)
        {
            object result;

            if (container.TryResolve(type, out result))
            {
                return result;
            }

            return null;
        }

        #endregion IDIContainerAdapter Members
    }

Obviously the registration of types would happen before passing in the container defined in the constructor above. So far so good... the apps will register their specific types. The problem is that the framework will also need to register some types and this is now where the problem lies:

How in the world am I supposed to let the app using the framework know what types to register? Firstly that sounds like a bad design to me.. something doesn't feel right about it; surely the app shouldn't need to care about it. Secondly, it's not something easy to do anyway...

I then did consider using a specific DI container for the framework and let the devs choose whatever they want to use (if anything) for their apps. However, I don't like the idea of 2 separate DI containers running together and researching this has confirmed it is not a good idea.

So I'm stuck wondering what is the best solution? Do I just force everyone to use my preferred container?

Upvotes: 2

Views: 400

Answers (3)

Hellraiser
Hellraiser

Reputation: 609

Well, I know it's an old topic, but I want to give my 2 cents here as I've implemented something like that in the past. Unfortunately, I don't have the code anymore, but believe me when I say it required A LOT of effort and, thinking of it now, I think it wasn't worth the effort as the IOC container was never changed for years (it was Castle Windsor and we only updated the version).

Anyways, before putting down a single line of code, write cases and risks. In particular:

  • Creating onle a Register method is very limiting. What if you have tons of dependencies to register? Registering all types in one or more assemblies or namespaces implementing a certain interface should be available as an option.
  • You may want to register all default implemntations without any other specification as well. That's to say all implementations called after the interface name (e.g IMyCompontent has a default implementation MyComponent)
  • I suppose you work in a distributed environment, with many teams. You have to give the possibility to every team of registering their own dependencies. This means you need to check for duplicates and get rid of them.
  • As a resolver you should use default Microsoft IOC implementation, as it's available across all .net language implementations
  • Of curse this must be generic and easy to replace a container with another without changing anything but the container adaptor. Read: changing your IoC container, MUST be totally transparent for the final users usign your api.
  • This is only to name a few of the aspects to consider. Hope it helps

    Upvotes: 0

    Prathap reddy
    Prathap reddy

    Reputation: 76

    My idea on this is a method can be added to interface say Register<T>(string type) which is used for adding registrations to DI Container.

    Create a Configuration class in your library expose a method to be called by the apps with the DIContaineradapter implementation in their composition root. Now since the library has handle of app DIContaineradapter inside the method call the register method to register all library related instances. Hence the apps need not know what types needs to be registered by the library except that they need to call a method on library passing the dicontaineradapter implementation.

    In this way the apps can be free to impement thier own dicontainers and also library can register its types through the app implemented dicontainer

    Upvotes: -1

    TeaDrivenDev
    TeaDrivenDev

    Reputation: 6629

    I faced the very same issue a while ago and found no really satisfactory solution back then. My last thought on that before work on the library ceased due to time constraints was to use TinyIoC for internal composition (which has the distinct advantage of being just a code file you include, not an additional assembly) and give users of the library the freedom to choose their own IoC container. But as you correctly say, that is not without its issues.

    From today's perspective, I think there are two reasonably feasible options:

    • As the library is for use within the company, I think locking it to a specific container should not be a huge problem, because there should be a consensus anyway on which container will generally be used for projects. As with any other library, you won't want every project to use the first thing that happened to bubble up in Google on the day it was started. Look at different containers and their features and make an informed company-wide decision. (I personally think you can't go wrong with Autofac.) If you have existing code that uses a number of different containers, replacing them with the one you pick should not be difficult of they're used correctly.

    • Even if your library code is composable, there will probably be a few specific object setups that will be very common for normal use. In that case, it may make sense to expose those in "pre-composed" form through facade classes, and if someone has more specific requirements, they can still roll their own from all the parts you provide. For example, AutoFixture uses this method to make usage very straightforward, although its components are highly composable.

    If you still think you really need a container agnostic way to do object composition, you may want to look at Common Composition before building essentially the same thing yourself.

    Upvotes: 3

    Related Questions