views:

1522

answers:

5

I'm pondering the design of a C# library, that will have several different high level functions. Of course, those high-level functions will be implemented using the SOLID class design principles as much as possible. As such, there will probably be classes intended for consumers to use directly on a regular basis, and "support classes" that are dependencies of those more common "end user" classes.

The question is, what is the best way to design the library so it is:

  • DI Agnostic - Although adding basic "support" for one or two of the common DI libraries (StructureMap, Ninject, etc) seems reasonable, I want consumers to be able to use the library with any DI framework.
  • Non-DI usable - If a consumer of the library is using no DI, the library should still be as easy to use as possible, reducing the amount of work a user has to do to create all these "unimportant" dependencies just to get to the "real" classes they want to use.

My current thinking is to provide a few "DI registration modules" for the common DI libraries (e.g a StructureMap registry, a Ninject module), and a set or Factory classes that are non-DI and contain the coupling to those few factories.

Thoughts?

+1  A: 

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

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

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

Igor Zevaka
+3  A: 

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

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

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

More details in this blog post.

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

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

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

Mauricio Scheffer
+4  A: 

The term "dependency injection" doesn't specifically have anything to do with an IoC container at all, even though you tend to see them mentioned together. It simply means that instead of writing your code 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 do two things when you write your code:

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

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

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

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

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

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

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

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

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

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

Aaronaught
The real abstraction for a container is IServiceProvider, not IContainer.
Mauricio Scheffer
@Mauricio: You're right of course, but try explaining to someone who's never used the LWC system why `IContainer` is not actually the container in one paragraph. ;)
Aaronaught
+30  A: 

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

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

Program to an interface, not an implementation

This principle is actually a quote (from memory though) from 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 Principle in DI terms says: Don't call the DI Container, it'll call you.

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

Use Constructor Injection

When you need a dependency, ask 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 the Service class guarantees its invariants. Once an instance is created, the dependency is guaranteed to be available because of the combination of the Guard Clause and the readonly keyword.

Use Abstract Factory if you need a short-lived object

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

See this for more information.

Compose only at the Last Responsible Moment

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

More details here:

Simplify using a Facade

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

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

public class MyFacade
{
    private IMyInterface 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 would allow a user to create a default Foo by writing

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

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

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

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

Mark Seemann
While this sounds great on paper, in my experience once you have lots of inner components interacting in complex ways, you end up with lots of factories to manage, making maintenance harder. Plus, factories will have to manage their created components' lifestyles, once you install the library in a real container this will conflict with the container's own lifestyle management. Factories and facades get in the way of the real containers.
Mauricio Scheffer
I have yet to find a project that does *all* of this.
Mauricio Scheffer
Well, that's how we develop software at Safewhere, so we don't share your experience...
Mark Seemann
I know about the general principles involved when designing something to use DI/IoC. In particular, I was asking about ways to design/simplify a library's *use* by other consumers, who may choose to use a container I favor, such as Ninject or StructureMap, or some alternative. What specific ways can I ease use of the "high-level" API while maintaining proper DI.The Facade approach is something I had concidered, but the question becomes: Do the facade class(es) depend directly on the implementations? If not, how do *they* get their dependencies injected?
Pete
I updated my answer by elaborating the section on Facades - I hope that makes it a bit clearer.
Mark Seemann
Is the MyFacade suitable for hiding an ioc container implementation ie. let the CreateFoo method resolve Foo from the container?
slamidtfyn
I think that the facades should be hand-coded because they represent known (and presumably common) combinations of components. A DI Container should not be necessary because everything can be wired up by hand (think Poor Man's DI). Recall that a Facade is just an optional convenience class for users of your API. Advanced users may still want to bypass the Facade and wire up the components to their own liking. They may want to use their own DI Contaier for this, so I think it would be unkind to force a particular DI Container upon them if they are not going to use it. Possible but not advisable
Mark Seemann
A: 

While my answer can't beat answer of Mark for sure, thought that it might be nice to see an example.

By any means - i'm not a ioc guru, but i guess Rashid is doing exactly what you are looking for quite nicely => check out his project.

Arnis L.