It's My Turn To Build An IoC Container In 15 Minutes and 33 Lines

on January 17th, 2008 at 9:16am , , 11 responses

Last night I've built a nice new tool called StaticMapGenerator which is used to generate a typed static resources site-map for ASP.NET sites (works for MonoRail, ASP.NET MVC and even WebForms).

I'll blog about it on a separate post in details.

Since I didn't want any dependency (but .NET 2.0 runtime) for the generator and the generated code, I couldn't use Windsor to IoC. That calls for a hand rolled simple IoC implementation

Ayende has already done it in 15 lines, but I wanted also to automagically set dependencies and have a simpler registration model.

so I've quickly hacked together a configurable DI resolver (a.k.a. IoC container) in 15 Minutes and 33 Lines Of Code. Call me a sloppy-coder, call me whadever-ya-like. It just works.

static class IoC 
{
   static readonly IDictionary<Type, Type> types = new Dictionary<Type, Type>();
  
   public static void Register<TContract, TImplementation>()
   {
      types[typeof(TContract)] = typeof(TImplementation);
   }
  
   public static T Resolve<T>()
   {
      return (T)Resolve(typeof(T));
   }

  
   public static object Resolve(Type contract)
   {
      Type implementation = types[contract];
  
      ConstructorInfo constructor = implementation.GetConstructors()[0];
  
      ParameterInfo[] constructorParameters = constructor.GetParameters();
  
      if (constructorParameters.Length == 0)
         return Activator.CreateInstance(implementation);
  
      List<object> parameters = new List<object>(constructorParameters.Length);
  
      foreach (ParameterInfo parameterInfo in constructorParameters)
         parameters.Add(Resolve(parameterInfo.ParameterType));
  
      return constructor.Invoke(parameters.ToArray());
   }
}

Ok, I've cheated. You'd need using statements too, but you can see that I was generous enough with newlines ...

Usage:

Given those:

public interface IFileSystemAdapter { }
 
public class FileSystemAdapter : IFileSystemAdapter { }
 
public interface IBuildDirectoryStructureService { }
 
public class BuildDirectoryStructureService : IBuildDirectoryStructureService
{
   IFileSystemAdapter fileSystemAdapter;
   public BuildDirectoryStructureService(IFileSystemAdapter fileSystemAdapter)
   {
      this.fileSystemAdapter = fileSystemAdapter;
   } }

You can do that:

IoC.Register<IFileSystemAdapter, FileSystemAdapter>();
 
IoC.Register<IBuildDirectoryStructureService, BuildDirectoryStructureService>();
 
IBuildDirectoryStructureService service = IoC.Resolve<IBuildDirectoryStructureService>();
You need not worry about supplying the BuildDirectoryStructureService with an implementation for the service it depends on, but only to register an implementation for that service.
Keith Keith on January 21st, 2009 at 5:03am
This is really cool. It would be cool if you could write a super simple console application that would show a working example. That would definitely help folks that are new to the topic. Cheers!
Ken&#32;Egozi Ken Egozi on January 25th, 2009 at 1:58pm
@Keith - great idea. I have very little free time these days though, but I might find a little time for that.
Cathal&#32;McHale Cathal McHale on June 29th, 2009 at 7:11am
Presumably an out-of-the-box IoC container like Castle Windsor would store object instances in the session?? (particularly for objects with a singleton life-cycle type).How would this work in your solution?Also what would you propose in a solution where session state is turned off? (e.g. an SOA architecture where scalability is of primary importance).Cheers,Cathal.
Ken&#32;Egozi Ken Egozi on June 29th, 2009 at 3:45pm
@Cathal:Singletons are not Per-Session - they are single per the container lifetime.In Windsor there is a PerWebRequest lifestyle, which stores instances on the Context.Items collection. Adding a PerSession lifestyle shouldn't be too complex - Windsor is super open to extension.Anyhoo - my solution is to use a proper Container when you can; Windsor is a proper one to say the least. I wouldn't have used a home grown solution (even mine) if I had another choice.
Cathal&#32;McHale Cathal McHale on July 3rd, 2009 at 8:42am
@Ken:Thanks for the reply. I accept the points you are making. Perhaps I will reword the question a little:- Let's assume that Windsor won't scale to the level we want it (I'm not saying it won't - that remains to be tested. But for the sake of argument lets assume it won't).- Let's assume their is a round-robin type load balancing mechanism in place (don't have a clue about this, but I'm told this is what's gonna be in place).- So assume that each consecutive request is hitting a different server. However there will be a shared memory cache that can be availed of by all servers.So an important job of any IoC container is to control life-cycle such that instantiation of objects is kept to a minimum. So whether this be per request, or once for the entire application...My question is: Given the architecture described above, how would your container (or indeed Windsor) achieve it's purpose? When would it be necessary to fetch from the shared memory space? When can things remain stateless?So I guess I'm wondering what goes on inside the black box of an IoC container? Are it's mechanisms suited to a stateless environment? I have indeed used Windsor within ASP.MVC before and you're right it does kick ass, and couldn't be made simpler with the work of MvcContrib. But certainly IoC's can slow things down by small amounts. When we scale to millions of users, how can we avoid these small amounts scaling also, and rendering the app unusable?Thanks,Cathal.
Ken&#32;Egozi Ken Egozi on July 3rd, 2009 at 1:06pm
@Cathal - imo scalability is not about Speed as it's about throughput.as for lifecycle - you're 100% correct. Instantiations should be kept to minimum. At work, the default lifestyle for registered components is Singleton - which in turn pushes all of us to write our components without shared state, thus helping us achieve better level of distribution => more scalability.Sometimes though, components does need some state.Let's consider a website that holds a certain amount of data per user in a session, and let's say that bringing it from the shared cache on each request is too heavy netwrok-wise and/or time-wise.What I'd do is to have that data loaded in the first request of the user's session from the shared cache, then store it on the machine's local cache. I'd then apply a loose sticky session semantics on the load balancer. I'd still avoid session *state*, and writes will go to the shared data, not to the local cache. that way I won't loose any data if the sticky session break, however I'd still have a good local cache hit ratio, to help in making things snappier.That said, if the session data is small enough, and fetching from the shared cache on each request is fast enough, the much simpler stateless model would still be my choice.now this has nothing to do with whether you use a container or not. The instantiation itself is not such a heavy thing, and you're probably not instantiating millions of instances at once, so the latency is totally negligible.
Brad Brad on November 21st, 2009 at 4:22am
You can turn List<object> parameters = new List<object>(constructorParameters.Length); foreach (ParameterInfo parameterInfo in constructorParameters) parameters.Add(Resolve(parameterInfo.ParameterType)); return constructor.Invoke(parameters.ToArray());into return constructor.Invoke(constructorParameters.Select(parameterInfo => Resolve(parameterInfo.ParameterType)).ToArray() );
Ken&#32;Egozi Ken Egozi on November 23rd, 2009 at 4:09am
@Brad - you are 100% correct, however this code was .NET 2.0, so c#3 was not part of the mix
Marcel&#32;Valdez Marcel Valdez on June 26th, 2011 at 3:42am

@Cathal McHale: I think you're confusing the Repository Pattern [another form of Inversion of Containers], which controls precisely object life-cycle, with a Dependency Injection implementation.

A Dependency Injection (as defined by Martin Fowler) implementation only has to 'concern' about instantiating or providing sub-types of a super-type (normally a concrete class of an abstract class, but not necessarily). Not about how long the object it retrieves or instantiates live.

It is possible (and probable) that other frameworks provide more functionality and go ahead provide you with a Object Repository, but that's their choice. A Dependency Injection, per-se, needs not provide you with that functionality.

Nguyen Nguyen on June 30th, 2011 at 8:08pm

that was awesome! it help me understand a whole lot about ioc. thank you







Comment preview:

Follow

Statistics

Posts count:
447
Comments:
951