Sunday, October 17, 2010

Simple C# inverse of control

I try to avoid third party tools whenever possible. Almost every time I used one, I had to deal with several updates, filtering functionality too complex for my needs, lack of support or maintenance over the time an so on.
I'm using a lot of Domain Driven Design lately and I really like it. Plus, I use a technique called inverse of control which allows dealing only with interfaces on Web applications. This is a great deal, because I'm able to use the implementation I need (Web, service or even mock-ups during development), plus it makes testing much easier.
There are a lot of third party components implementing inverse of control: Castle Windsor, StructureMap, Spring.NET, Autofac, and so on.
But since I don't like to use third party tools, I'm more inclined to use a simple version good enough for my project needs. There are several code examples on the net, like IoC container in 15 lines or IocContainer in 15 minutes.
Here is a simple version I've build, which allows the Interfaces to be automatically registered, son I don't have to write additional code or to use a configuration file. Everything is much simpler, so this is my thing.

    public static class SimpleContainer
    {
        private static IDictionary<Type, Type> _types = null;
        public static void AutoRegister()
        {
            if (_types == null)
            {
                _types = new Dictionary<Type, Type>();
                Assembly assembly = Assembly.GetExecutingAssembly();
                foreach (Type contractType in assembly.GetTypes())
                {
                    if (contractType.IsInterface)
                    {
                        Type implementationType = assembly.GetType(contractType.FullName.Replace("Interfaces.I", "Implementation."));
                        if (implementationType != null)
                            _types[contractType] = implementationType;
                    }
                }
            }
        }
        public static void Register<TContract, TImplementation>()
        {
            if (_types == null) AutoRegister();
            _types[typeof(TContract)] = typeof(TImplementation);
        }
        public static T Resolve<T>()
        {
            return (T)Resolve(typeof(T));
        }
        public static object Resolve(Type contract)
        {
            if (_types == null) AutoRegister();
            Type implementation = _types[contract];
            ConstructorInfo constructor = implementation.GetConstructors()[0];
            ParameterInfo[] constructorParameters = constructor.GetParameters();
            return constructorParameters.Length == 0
                ? Activator.CreateInstance(implementation)
                : constructor.Invoke(constructorParameters.Select(parameterInfo => Resolve(parameterInfo.ParameterType)).ToArray());
        }

Prety simple. The only restriction is that you have to use a naming convention for default implementations.
  • Interface example: YourNamespace.Interfaces.ISomeObject
  • Implementation example: YourNamespace.Implementation.SomeObject
Of course, you may use the auto registration and then override some interface registration (for mockups for instance).

The code to use after is really simple, a single line. No config, no extra registration lines, no nothing. Just use the resolve method and you're done.
ISomeObject someObject = Container.Resolve<ISomeObject>();


Feel free to use this piece of code if it fits your needs.

No comments:

Post a Comment