In a previous post, Assembling a utility assembly, part 2: IOC Container, I wrote
I thought of adding support for a more fluent interface, since “all the other ones do it” … But I decided that I didn’t need it.
Well, hmmm… After that post I was a bit curious how things would actually look, design-wise, if they were fluent-ized. So I changed the design and then I didn’t want to go back, because I liked the new one better.
The old way of registering types with the IOC container:
public sealed class WeeContainer
{
...
public void Bind<TTarget>() { ... }
public void Bind<TTarget>(Lifetime lifetime) { ... }
public void Bind<TTarget, TSource>() { ... }
public void Bind<TTarget, TSource>(Lifetime lifetime) { ... }
...
}
And the new way:
public sealed class WeeContainer
{
...
public IBindingTarget Bind<T>() { ... }
...
}
The most obvious difference between the old and new WeeContainer is that its public interface has become much lighter. This has a cost associated with it: The new design has introduced several new types that add complexity to the code base and that requires new tests.
Using the new WeeContainer is probably easier, since there’s only one public method that registers types with the container. Before, there were four methods to choose from.
If a type should be resolved by itself, meaning that it’s instantiatable, using the default lifetime, then calling WeeContainer.Bind<T>() is enough. But if it should be resolved by another type or use another lifetime, then the return value from WeeContainer.Bind<T>() needs to be used, namely the IBindingTarget:
public interface IBindingTarget
{
IBindingSource To<T>();
IBindingTarget With(Lifetime lifetime);
}
From IBindingTarget we can conclude that a type registered with WeeContainer.Bind<T>() can get a lifetime other than the default if IBindingTarget.With(Lifetime) is called. The type can also be resolved by another type by calling IBindingTarget.To<T>():
var container = new WeeContainer(); container.Bind<Foo>().With(Lifetime.Singleton); container.Bind<IBar>().To<Bar>();
Since IBindingTarget.With(Lifetime) returns the IBindingTarget instance, we can fluently add a call to IBindingTarget.To<T>():
container.Bind<IBar>().With(Lifetime.Singleton).To<Bar>();
As you can see, IBindingTarget.To<T>() returns an IBindingSource instance:
public interface IBindingSource
{
void With(Lifetime lifetime);
}
This interface makes it possible to change the order of the above fluent call to
container.Bind<IBar>().To<Bar>().With(Lifetime.Singleton);
which often feels more natural.
I will look at the implementation of this new fluent interface for WeeContainer and its friends in another post.

