[ACCEPTED]-Dependency Inject (DI) "friendly" library-inversion-of-control

Accepted answer
Score: 370

This is actually simple to do once you understand 40 that DI is about patterns and principles, not technology.

To design 39 the API in a DI Container-agnostic way, follow 38 these general principles:

Program to an interface, not an implementation

This principle 37 is actually a quote (from memory though) from 36 Design Patterns, but it should always be your real goal. DI is just a means to achieve that end.

Apply the Hollywood Principle

The Hollywood 35 Principle in DI terms says: Don't call the DI Container, it'll call you.

Never directly 34 ask for a dependency by calling a container 33 from within your code. Ask for it implicitly 32 by using Constructor Injection.

Use Constructor Injection

When you need a dependency, ask 31 for it statically through the constructor:

public class Service : IService
{
    private readonly ISomeDependency dep;

    public Service(ISomeDependency dep)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        this.dep = dep;
    }

    public ISomeDependency Dependency
    {
        get { return this.dep; }
    }
}

Notice how 30 the Service class guarantees its invariants. Once 29 an instance is created, the dependency is 28 guaranteed to be available because of the 27 combination of the Guard Clause and the 26 readonly keyword.

Use Abstract Factory if you need a short-lived object

Dependencies injected with Constructor 25 Injection tend to be long-lived, but sometimes 24 you need a short-lived object, or to construct 23 the dependency based on a value known only 22 at run-time.

See this for more information.

Compose only at the Last Responsible Moment

Keep 21 objects decoupled until the very end. Normally, you 20 can wait and wire everything up in the application's 19 entry point. This is called the Composition Root.

More details 18 here:

Simplify using a Facade

If you feel that the resulting API 17 becomes too complex for novice users, you 16 can always provide a few Facade classes that encapsulate 15 common dependency combinations.

To provide 14 a flexible Facade with a high degree of 13 discoverability, you could consider providing 12 Fluent Builders. Something like this:

public class MyFacade
{
    private IMyDependency dep;

    public MyFacade()
    {
        this.dep = new DefaultDependency();
    }

    public MyFacade WithDependency(IMyDependency dependency)
    {
        this.dep = dependency;
        return this;
    }

    public Foo CreateFoo()
    {
        return new Foo(this.dep);
    }
}

This 11 would allow a user to create a default Foo 10 by writing

var foo = new MyFacade().CreateFoo();

It would, however, be very discoverable 9 that it's possible to supply a custom dependency, and 8 you could write

var foo = new MyFacade().WithDependency(new CustomDependency()).CreateFoo();

If you imagine that the MyFacade 7 class encapsulates a lot of different dependencies, I 6 hope it's clear how it would provide proper 5 defaults while still making extensibility 4 discoverable.


FWIW, long after writing this 3 answer, I expanded upon the concepts herein 2 and wrote a longer blog post about DI-Friendly Libraries, and 1 a companion post about DI-Friendly Frameworks.

Score: 42

The term "dependency injection" doesn't 65 specifically have anything to do with an 64 IoC container at all, even though you tend 63 to see them mentioned together. It simply 62 means that instead of writing your code 61 like this:

public class Service
{
    public Service()
    {
    }

    public void DoSomething()
    {
        SqlConnection connection = new SqlConnection("some connection string");
        WindowsIdentity identity = WindowsIdentity.GetCurrent();
        // Do something with connection and identity variables
    }
}

You write it like this:

public class Service
{
    public Service(IDbConnection connection, IIdentity identity)
    {
        this.Connection = connection;
        this.Identity = identity;
    }

    public void DoSomething()
    {
        // Do something with Connection and Identity properties
    }

    protected IDbConnection Connection { get; private set; }
    protected IIdentity Identity { get; private set; }
}

That is, you 60 do two things when you write your code:

  1. Rely 59 on interfaces instead of classes whenever 58 you think that the implementation might 57 need to be changed;

  2. Instead of creating instances 56 of these interfaces inside a class, pass 55 them as constructor arguments (alternatively, they 54 could be assigned to public properties; the 53 former is constructor injection, the latter is property injection).

None of this 52 presupposes the existence of any DI library, and 51 it doesn't really make the code any more 50 difficult to write without one.

If you're 49 looking for an example of this, look no 48 further than the .NET Framework itself:

  • List<T> implements 47 IList<T>. If you design your class to use IList<T> (or 46 IEnumerable<T>), you can take advantage of concepts like 45 lazy-loading, as Linq to SQL, Linq to Entities, and 44 NHibernate all do behind the scenes, usually 43 through property injection. Some framework 42 classes actually accept an IList<T> as a constructor 41 argument, such as BindingList<T>, which is used for several 40 data binding features.

  • Linq to SQL and EF 39 are built entirely around the IDbConnection and related 38 interfaces, which can be passed in via the 37 public constructors. You don't need to 36 use them, though; the default constructors 35 work just fine with a connection string 34 sitting in a configuration file somewhere.

  • If 33 you ever work on WinForms components you 32 deal with "services", like INameCreationService or IExtenderProviderService. You don't 31 even really know what what the concrete 30 classes are. .NET actually has its own IoC 29 container, IContainer, which gets used for this, and 28 the Component class has a GetService method which is the actual 27 service locator. Of course, nothing prevents 26 you from using any or all of these interfaces 25 without the IContainer or that particular locator. The 24 services themselves are only loosely-coupled 23 with the container.

  • Contracts in WCF are 22 built entirely around interfaces. The actual 21 concrete service class is usually referenced 20 by name in a configuration file, which is 19 essentially DI. Many people don't realize 18 this but it is entirely possible to swap 17 out this configuration system with another 16 IoC container. Perhaps more interestingly, the 15 service behaviors are all instances of IServiceBehavior which 14 can be added later. Again, you could easily 13 wire this into an IoC container and have 12 it pick the relevant behaviors, but the 11 feature is completely usable without one.

And 10 so on and so forth. You'll find DI all 9 over the place in .NET, it's just that normally 8 it's done so seamlessly that you don't even 7 think of it as DI.

If you want to design 6 your DI-enabled library for maximum usability 5 then the best suggestion is probably to 4 supply your own default IoC implementation 3 using a lightweight container. IContainer is a great 2 choice for this because it's a part of the 1 .NET Framework itself.

Score: 4

EDIT 2015: time has passed, I realize now that this 43 whole thing was a huge mistake. IoC containers 42 are terrible and DI is a very poor way to 41 deal with side effects. Effectively, all 40 of the answers here (and the question itself) are 39 to be avoided. Simply be aware of side effects, separate 38 them from pure code, and everything else 37 either falls into place or is irrelevant 36 and unnecessary complexity.

Original answer 35 follows:


I had to face this same decision 34 while developing SolrNet. I started with the goal 33 of being DI-friendly and container-agnostic, but 32 as I added more and more internal components, the 31 internal factories quickly became unmanageable 30 and the resulting library was inflexible.

I 29 ended up writing my own very simple embedded IoC container while also providing 28 a Windsor facility and a Ninject module. Integrating the library with 27 other containers is just a matter of properly 26 wiring the components, so I could easily 25 integrate it with Autofac, Unity, StructureMap, whatever.

The 24 downside of this is that I lost the ability 23 to just new up the service. I also took a dependency 22 on CommonServiceLocator which I could have avoided (I might 21 refactor it out in the future) to make the 20 embedded container easier to implement.

More 19 details in this blog post.

MassTransit seems to rely on something 18 similar. It has an IObjectBuilder interface which is really 17 CommonServiceLocator's IServiceLocator with 16 a couple more methods, then it implements 15 this for each container, i.e. NinjectObjectBuilder and a regular 14 module/facility, i.e. MassTransitModule. Then it relies on IObjectBuilder to instantiate 13 what it needs. This is a valid approach 12 of course, but personally I don't like it 11 very much since it's actually passing around 10 the container too much, using it as a service 9 locator.

MonoRail implements its own container as well, which implements 8 good old IServiceProvider. This container is used throughout 7 this framework through an interface that exposes well-known services. To get the concrete 6 container, it has a built-in service provider locator. The Windsor facility points this service 5 provider locator to Windsor, making it the 4 selected service provider.

Bottom line: there 3 is no perfect solution. As with any design 2 decision, this issue demands a balance between 1 flexibility, maintainability and convenience.

Score: 1

What I would do is design my library in 15 a DI container agnostic way to limit the 14 dependency on the container as much as possible. This 13 allows to swap out on DI container for another 12 if need be.

Then expose the layer above the 11 DI logic to the users of the library so 10 that they can use whatever framework you 9 chose through your interface. This way they 8 can still use DI functionality that you 7 exposed and they are free to use any other 6 framework for their own purposes.

Allowing 5 the users of the library to plug their own 4 DI framework seems a bit wrong to me as 3 it dramatically increases amount of maintenance. This 2 also then becomes more of a plugin environment 1 than straight DI.

More Related questions