Introduction
Whenever I start a new “pet” project I usually *want* to use IoC, but I don’t really want the hassle of taking a dependency on a container, or adding another binary to my project. Usually this leaves me using “poor man’s IoC”, whereby I define dependencies in my constructors, but I pass them in manually or construct them using constructor chaining.
I’ve thought for some time that it would be useful to put together a container that fits the bill for that scenario, but also attempts to “lower the barrier of entry” for developers that are new to IoC, and may be scared off by the “big boys”. A couple of weeks ago @DotNetWill posted his simplified container, and that gave me the kick up the backside I needed to create my own 🙂
Introducing TinyIoC
Now you might ask “do we really need *another* IoC container?” – and it’s a reasonable question. We already have Unity, Ninject, StructureMap, AutoFac.. the list goes on. TinyIoC makes no attempt to go “head to head” with any of these other containers, instead TinyIoC has been designed to fulfil a single key requirement:
To lower the “level of entry” for using an IoC container; both for small projects, and developers who are new to IoC.
To that end, TinyIoC attempts to stick the following core principals:
- Simplified Inclusion. No assembly to reference, no binary to worry about, just a single cs file you can include in your project and you’re good to go. It also works on Mono, and on MonoTouch for the iPhone / iPad.
- Simplified Setup. With auto-resolving of concrete types and an “auto registration” option for interfaces, setup is a piece of cake. It can be reduced to 0 lines for concrete types, or 1 line if you have any interface dependencies.
- Simple, “Fluent” API. Just because it’s Tiny, doesn’t mean it has no features. A simple “fluent” API gives you access to the more advanced features, like specifying singleton/multi-instance, strong or weak references or forcing a particular constructor.
The following snippet gives an example of the simplified setup:
// TinyIoC provides a lazyily constructed singleton // version of itself if you want to use it. var container = TinyIoCContainer.Current; // By default we can resolve concrete types without // registration var instance = container.Resolve<MyConcreteType>(); // We can automatically register all concrete types // and interfaces with a single call. container.AutoRegister(); var implementation = container.Resolve<IMyInterface>();
So Where Now?
If you want to grab the source, read the tests, or take a look at some more complex examples of the API, wander on over to the homepage on bitbucket. I’m happy to take comments and suggestions – just grab me on Twitter or ping me an email from the contact form.
Over the next few weeks I will be posting a series of “beginners” articles on the hows and whys of IoC. I will be using TinyIoC specifically, but the majority of concepts and content can equally apply to other containers. I may even do a “File, New” screencast to show how “un-scary” this stuff is, and how it doesn’t really add any extra development effort,
What happens if there are multiple implementations of IMyInterface in the AutoRegister example?
“Last one wins” in that scenario. I did consider throwing an exception, or even adding named registrations, but thought both of those would be significantly less friendly.
In my mind, if you hit this issue you can either:
a. Get rid of autoregistration and switch to a more “standard” manual approach.
or, my preferred option:
b. Auto-register to get the vast majority of registrations done, then manually register your exceptions:
container.AutoRegister();
container.Register();
container.Register("OtherOne");
What about container.AutoRegister(DuplicateOption.ThrowException) / AutoRegister(DuplicateOption.Ignore)?
I think I’d like to keep the default to be “ignoring” duplicates, but it’s a nice idea to add an option for it – either that or return a boolean for whether any issues were found, then it’s up to you whether you do anything about it or not?
I’ll also add support for abstract base classes too, in the same way that interfaces get added.
How about make it even more configurable:
container.AutoRegister().AmbiguousResolutionBehaviour(types => types.First())
container.AutoRegister().AmbiguousResolutionBehaviour(types => throw new AmbiguousResolutionException(types))
container.AutoRegister().AmbiguousResolutionBehaviour(MyCustomMethodForResolvingWhichOneIWant)
I’ve changed AutoRegister so you can now get it to throw a meaningful exception if there’s duplicates. The exception message shows the interface/base class that has the clash, and also a comma separated list of all the potential implementations.
I could take that IEnumerable and pass it into a user specified delegate, but I’m not sure whether that’s taking AutoRegister too far or not – if there’s definite logic you need in your registrations, or if you need named registrations etc., then you might be best either switching to just Register, or mixing AutoRegister with some specific Register commands afterwards?
[…] Announcing: TinyIoC – An Easy to Use, Hassle Free, Inversion of Control Container – Steven Robbins announces his current ‘Pet Project’ to build a simple Inversion of Control Framework, and accompanying blog post series showing it in use and illustrating IOC concepts. Full source is available for this simple IOC container, and reader comments and suggestions are welcome. […]
It all looks pretty sweet and I love some of the ideas. Great work!
Really enjoying your posts recently Steve. Read half the code last night of TinyIoC and for it looks great. Didn’t understand why you’d put in all in one .cs until this post. Looking forward to the series.
Like it. You can keep it up to date with this http://www.adverseconditionals.com/2010/01/easy-peasy-web-dependencies-using-t4.html 🙂
It’d be pretty cool if, when faced with multiple implementations, it picked up the one in the launch assembly first. This way you can have a default implementation of a service in a common library, but override it in your different applications.
Thanks for the comment, that T4 thing is pretty cool 🙂
You can mix and match AutoRegister and Register, and even AutoRegister more than one assembly, so you could AutoRegister your common library first, then do it against your app assembly to copy over any customisations you have in there.
[…] Announcing: TinyIoC – An Easy to Use, Hassle Free, Inversion of Control Container Yet another IoC container for .NET. Sounds interesting though, will dig into it. […]
[…] I omitted one important feature, lifestyle management. This coupled with the release of a feature full TinyIOC has made me re-evaluate my position on not adding too many features to OCInject. Release 2 of […]
Hi
Loving this IoC – works perfect in MT – going to give it a whirl in monodroid too. One thing I can see being an advantage would be to filter the auto reg so that it doesn’t try and register everything. Something like the Windsor API:
container.Register(
AllTypes.Of()
.FromAssembly(Assembly.GetExecutingAssembly())
);
Though that may be a bit much – some way of filtering would be good – unless the overhead for large projects is minimal?
w://
There’s a few overloads where you can specify specific assemblies to scan, rather than everything in the app domain which should do the same as the Windsor example you posted.
I have considered adding a predicate overload to dictate whether a type is registered or not, but I’m not entirely sure if it’s overkill or not.
Hi,
is possible enumerate all types of in container… like GetAll()
thx
Not yet, but it’s on my list as the next feature to add, both a ResolveAll and the ability to have an IEnumerable dependency injected.
This test fails, with stackoverflow exception. Because the container contains a self reference.
[TestMethod]
public void Register_NamedInstanceRegistration_CanRegister_And_Dispose()
{
var container = UtilityMethods.GetContainer();
var item = new TestClassDefaultCtor();
container.Register(item, “TestName”);
container.Dispose();
}
Hi There,
Thanks for pointing this out, it’s now been fixed.
Cheers
Great! I always missed simple IoC for my introductionary O-O programming sessions
What am I doing wrong? I am using MonoTouch
var container = TinyIoCContainer.Current;
container.Register();
var v = container.Resolve();
// Simulator crashes, no exception, no callstack
So I have determined that tiny ioc is blowing the stack by infinitely recursing while trying to resolve.
My last code was not quite sufficient. Should look like this:
var container = TinyIoCContainer.Current;
container.Register();
var v = container.Resolve();
// Infinite recursion in ‘Resolve’ and blows up.
Thoughts?
Why is it stripping out my templates in my code examples?
It should be .Register with two template types Interface and Concrete
.Resolve with one template type of Interface
If it’s overflowing the stack then you have a cyclic dependency issue.
Hi, I’m looking at TinyIoC for Cross platform IoC for mobile development. I want to try simply some of the plumbing needed when creating a new app. To do this i need to be able to intercept methods and properties and wrap them with certain aspects. Is there a way to extend TinyIoC to be able to register/get a type with an interceptor?
It’s a pitty that multiple implementations won’t register themselves also.
I have an IPayment interface used for multiple different payment types.
So why can I register them manually, and won’t it auto register?
I want to resolve all IPayment implementations in an IEnumerable anyways.
Why did you chose to silently pick the last one and not just return an IEnumerable?
Regards
Casper
Tried the manual registering, but at resolving I end up with one implementation being resolved twice:
container.RegisterMultiple(new Type[] { typeof(CreditcardModule), typeof(PaypalModule) }).AsSingleton();
container.ResolveAll() gives me {CreditcardModule, CreditcardModule, PaypalModule}
How can this be?