Saturday, April 28, 2012

Creating my own dependency injection system II

This post is about extending my own dependency injection system with binding scopes.

In the previous post I showed a very basic DI system. Several things can be added to it, and today I will show how to add binding scopes. What is a binding scope? A binding scope is an indication of when and how the created instances of the DI system should be re-used.

For example, a singleton binding scope. With the singleton binding scope we indicate that once an instance of an object is created for a specific binding rule, no more instances should be created, and that same instance should be returned.

For now we are going to implement the following binding scopes:
  • Normal – An instance is created every time, is the default scope.
  • Singleton – An instance is created once and re-used after.
  • Thread – An instance is created per thread, and re-used after in that thread.
  • WebRequest – An instance is created per web request, and re-used after for that request.
  • WebSession – An instance is created per web session, and re-used after for that session.
  • WebCache – An instance is created when is not yet available in the caching of the web application.
The last three are obviously only applicable in a web application.

First things first. I updated the code from the previous post:
  1. The Factory.GetInstance<TInterface>() method has been renamed to Factory.ResolveInstance<TInterface>().
  2. Accordingly, the interface IInstanceCreator<TInterface> has been renamed to IInstanceResolver<TInterface>. Also, the Create() method has been renamed to Resolve().
  3. Accordingly, the class InstanceCreator<TInterface, TClass> has been renamed to InstanceResolver<TInterface, TClass>. And, of course, the method has been renamed as well.
Good, that’s done. Onto the binding scopes. To implement the binding scopes we basically need to do two things:
  1. Add the ability to indicate the binding scope on a binding rule using the fluent interface.
  2. Update the binding rule or instance resolver so that the binding scope is incorporated.
I want to update the fluent interface in the following way.
   1: Factory.Bind<ICommunicator>().To<WirelessCommunicator>()
   2:     .WithScope(BindingScope.Singleton);
The first one is simple. First we define a binding scope enumeration.
   1: namespace MyDI {
   2:     public enum BindingScope {
   3:         Normal,
   4:         Singleton,
   5:         Thread,
   6:         WebRequest,
   7:         WebSession,
   8:         WebCache
   9:     }
  10: }
Now we can add the binding scope to the fluent interface.
   1: namespace MyDI {
   2:     public interface IBindingScopeIndicator {
   3:         void WithScope(BindingScope scope);
   4:     }
   5: }
   1: namespace MyDI {
   2:     public interface IBindingRule { }
   3:  
   4:     public interface IBindingRule<TInterface> : IBindingRule {
   5:         IBindingScopeIndicator To<TClass>()
   6:             where TClass : class, TInterface, new();
   7:     }
   8: }
   1: using System;
   2:  
   3: namespace MyDI {
   4:     class BindingRule<TInterface> : IBindingRule<TInterface>, IBindingScopeIndicator {
   5:         private IInstanceResolver<TInterface> _resolver;
   6:  
   7:         public IBindingScopeIndicator To<TClass>() where TClass : class, TInterface, new() {
   8:             _resolver = new InstanceResolver<TInterface, TClass>();
   9:             return this;
  10:         }
  11:  
  12:         public TInterface Create() {
  13:             if (_resolver != null) {
  14:                 return _resolver.Resolve();
  15:             }
  16:             throw new InvalidOperationException();
  17:         }
  18:  
  19:         public void WithScope(BindingScope scope) {
  20:             // TODO
  21:             throw new NotImplementedException();
  22:         }
  23:     }
  24: }
So what did we do? We added an interface with which to indicate the binding scope. This interface has to be returned after the IBindingRule<TInterface>.To<TClass>() method, so this method was updated to return an IBindingScopeIndicator interface.

Last, the binding rule class was updated to reflect the changes in the binding rule interface, and it now also implements the IBindingScopeIndicator interface. The implementation of the method IBindingScopeIndicator.WithScope(BindingScope) will be done later.

We can now update the unittests, and add a failing unittest for the singleton scope.
   1: using Microsoft.VisualStudio.TestTools.UnitTesting;
   2: using MyDI;
   3:  
   4: namespace MyDI_Tests {
   5:     [TestClass]
   6:     public class FactoryTests {
   7:         [TestMethod]
   8:         public void Test_normal_type_correct() {
   9:             Factory.Clear();
  10:             Factory.Bind<ICommunicator>().To<WirelessCommunicator>();
  11:             var test = Factory.ResolveInstance<ICommunicator>();
  12:             Assert.IsNotNull(test);
  13:             Assert.AreEqual(typeof(WirelessCommunicator), test.GetType());
  14:         }
  15:  
  16:         [TestMethod]
  17:         public void Test_normal_scope_check() {
  18:             Factory.Clear();
  19:             Factory.Bind<ICommunicator>().To<WirelessCommunicator>();
  20:             var test1 = Factory.ResolveInstance<ICommunicator>();
  21:             var test2 = Factory.ResolveInstance<ICommunicator>();
  22:             Assert.IsNotNull(test1);
  23:             Assert.AreEqual(typeof(WirelessCommunicator), test1.GetType());
  24:             Assert.IsNotNull(test2);
  25:             Assert.AreEqual(typeof(WirelessCommunicator), test2.GetType());
  26:             Assert.AreNotEqual(test1, test2);
  27:         }
  28:  
  29:         [TestMethod]
  30:         public void Test_singleton_scope_check() {
  31:             Factory.Clear();
  32:             Factory.Bind<ICommunicator>().To<WirelessCommunicator>().WithScope(BindingScope.Singleton);
  33:             var test1 = Factory.ResolveInstance<ICommunicator>();
  34:             var test2 = Factory.ResolveInstance<ICommunicator>();
  35:             Assert.IsNotNull(test1);
  36:             Assert.AreEqual(typeof(WirelessCommunicator), test1.GetType());
  37:             Assert.IsNotNull(test2);
  38:             Assert.AreEqual(typeof(WirelessCommunicator), test2.GetType());
  39:             Assert.AreEqual(test1, test2);
  40:         }
  41:     }
  42: }
The first two unittests pass, the last one fails.

image

We now have to update the InstanceResolver class to do something with the given binding scope.

To prevent our InstanceResolver class from cluttering up, I am going to define an interface called IScopeHandler<TClass>. The purpose of this interface is to, obviously, handle the scope of a binding rule. Therefore we will have implementations for every binding scope, NormaleScopeHandler, SingletonScopeHandler, ThreadScopeHandler, and so forth. In this post I will only show the class for the first three binding scopes, the rest will be in the code that can be downloaded at the bottom of the post.

Lets define the scope handler interface, and create the scope handler classes.
   1: namespace MyDI {
   2:     public interface IScopeHandler<TClass> where TClass : class, new() {
   3:         TClass GetInstance();
   4:     }
   5: }
   1: namespace MyDI.ScopeHandlers {
   2:     public class NormalScopeHandler<TClass> : IScopeHandler<TClass> where TClass : class, new() {
   3:         public TClass GetInstance() {
   4:             return new TClass();    
   5:         }
   6:     }
   7: }
   1: using System;
   2:  
   3: namespace MyDI.ScopeHandlers {
   4:     public class SingletonScopeHandler<TClass> : IScopeHandler<TClass> where TClass : class, new() {
   5:         private Lazy<TClass> _value = new Lazy<TClass>(() => new TClass(), true);
   6:  
   7:         public TClass GetInstance() {
   8:             return _value.Value;
   9:         }
  10:     }
  11: }
   1: using System;
   2:  
   3: namespace MyDI.ScopeHandlers {
   4:     public class ThreadScopeHandler<TClass> : IScopeHandler<TClass> where TClass : class, new() {
   5:         [ThreadStatic]
   6:         private static TClass _value = null;
   7:  
   8:         public TClass GetInstance() {
   9:             if (_value == null)
  10:                 _value = new TClass();
  11:             return _value;
  12:         }
  13:     }
  14: }
So what is exactly happening here?

Interface IScopeHandler<TClass> This interface defines a scope handler. A scope handler has only one method, which is to get an instance of the class-type.

Class NormalScopeHandler<TClass> This class creates an object every time the method GetInstance() is called.

Class SingletonScopeHandler<TClass> This class creates an object once, and uses it every time the method GetInstance() is called.

Class ThreadScopeHandler<TClass> This class has a field with a ThreadStatic attribute, and that field is used to hold an object per thread, and that object is used every time the method GetInstance() is called. Logically this binding scope is very similar to the singleton binding scope. The implementation is a bit different though.

Now that we have scope handlers, we need a handy place to get scope handler instances. For this, a scope handler factory is created.
   1: using System;
   2: using MyDI.ScopeHandlers;
   3:  
   4: namespace MyDI {
   5:     public static class ScopeHandlerFactory {
   6:         private const string SCOPEHANDLERNOTREGISTEREDEXCEPTIONMESSAGE = "The specified scope handler was not registered.";
   7:  
   8:         public static IScopeHandler<TClass> GetScopeHandler<TClass>(BindingScope scope) where TClass : class, new() {
   9:             switch (scope) {
  10:                 case BindingScope.Normal:
  11:                     return new NormalScopeHandler<TClass>();
  12:                 case BindingScope.Singleton:
  13:                     return new SingletonScopeHandler<TClass>();
  14:                 case BindingScope.Thread:
  15:                     return new ThreadScopeHandler<TClass>();
  16:                 case BindingScope.WebRequest:
  17:                     return new WebRequestScopeHandler<TClass>();
  18:                 case BindingScope.WebSession:
  19:                     return new WebSessionScopeHandler<TClass>();
  20:                 case BindingScope.WebCache:
  21:                     return new WebCacheScopeHandler<TClass>();
  22:             }
  23:             throw new InvalidOperationException(SCOPEHANDLERNOTREGISTEREDEXCEPTIONMESSAGE);
  24:         }
  25:     }
  26: }
Now we’re getting somewhere. We have scope handlers, and a class to get them. Now we can update the InstanceResolver class. And we can do that very easily.
   1: namespace MyDI {
   2:     class InstanceResolver<TInterface, TClass> : IInstanceResolver<TInterface> where TClass : class, TInterface, new() {
   3:         private IScopeHandler<TClass> _scopeHandler = ScopeHandlerFactory.GetScopeHandler<TClass>(BindingScope.Normal);
   4:  
   5:         public TInterface Resolve() {
   6:             return _scopeHandler.GetInstance();
   7:         }
   8:  
   9:         public void SetScope(BindingScope scope) {
  10:             _scopeHandler = ScopeHandlerFactory.GetScopeHandler<TClass>(scope);
  11:         }
  12:     }
  13: }
A default normal scope handler is retrieved when the instance resolver is instanced. When necessary, it is replaced with another scope handler based on the given binding scope.

Lastly, we can update the BindingRule class to implement the method SetScope().
   1: using System;
   2:  
   3: namespace MyDI {
   4:     class BindingRule<TInterface> : IBindingRule<TInterface>, IBindingScopeIndicator {
   5:         private IInstanceResolver<TInterface> _resolver;
   6:  
   7:         public IBindingScopeIndicator To<TClass>() where TClass : class, TInterface, new() {
   8:             _resolver = new InstanceResolver<TInterface, TClass>();
   9:             return this;
  10:         }
  11:  
  12:         public TInterface Create() {
  13:             if (_resolver != null) {
  14:                 return _resolver.Resolve();
  15:             }
  16:             throw new InvalidOperationException();
  17:         }
  18:  
  19:         public void WithScope(BindingScope scope) {
  20:             _resolver.SetScope(scope);
  21:         }
  22:     }
  23: }
Now all unittests are passing again. Excellent!

image

To add the other binding scopes, all we have to do is write the specific scope handler class, and add it to the factory.

The code can be downloaded here.

I’m not sure what will be next, maybe constructor-injection, but not everyone thinks this is a good thing.

Tuesday, March 13, 2012

Creating my own dependency injection system

This post is about creating my own dependency injection system.

Dependency injection allows us to inject dependencies at run-time instead of compile-time. I’m going to start with the simple version, which I’ll describe using the following requirements:

  1. I want to define a binding between an interface and a class using generics. Obviously the class must implement the interface.
    • This means that all dependencies must be known at compile-time. So for now I’m not going to include any run-time loaded types based on string values, or any other similar methods.
  2. I want to use fluent interfaces to define the binding.
  3. I want to be able to resolve an instance of an object by specifying the interface type.

Going from the list above I could come up with the following way to define a binding and to get an instance of an class:

   1: namespace MyDI_Tests {
   2:     public static class Test {
   3:         public static void DoTest() {
   4:             Factory.Bind<ITestObject>().To<TestObject>();
   5:             var test = Factory.GetInstance<ITestObject>();
   6:             // test should be of type TestObject
   7:         }
   8:     }
   9: }

In the text below, I will reference to the ITestObject type as the interface-type and the TestObject type as the class-type.

All right, lets make it happen. First we need a Factory class.

   1: namespace MyDI {
   2:     public static class Factory {
   3:         public static IBindingRule<TInterface> Bind<TInterface>() {
   4:             // TODO
   5:             throw new NotImplementedException();
   6:         }
   7:  
   8:         public static TInterface GetInstance<TInterface>() {
   9:             // TODO
  10:             throw new NotImplementedException();
  11:         }
  12:     }
  13: }

We are also in need of an interface to define a binding rule. We’ll worry about the implementation later.

   1: namespace MyDI {
   2:     public interface IBindingRule<TInterface> {
   3:         void To<TClass>() where TClass : class, TInterface, new();
   4:     }
   5: }

And presto, now we can define binding rules in the factory. Let’s create a unittest. We need a dummy interface and class, and a unittest class.

   1: namespace MyDI_Tests {
   2:     interface ICommunicator {
   3:         void SendCommand(string command);
   4:     }
   5:  
   6:     class WirelessCommunicator : ICommunicator {
   7:         public void SendCommand(string command) {
   8:             throw new System.NotImplementedException();
   9:         }
  10:     }
  11: }
   1: namespace MyDI_Tests {
   2:     [TestClass]
   3:     public class FactoryTests {
   4:         [TestMethod]
   5:         public void TestMethod1() {
   6:             Factory.Bind<ICommunicator>().To<WirelessCommunicator>();
   7:             var test = Factory.GetInstance<ICommunicator>();
   8:             Assert.IsNotNull(test);
   9:             Assert.AreEqual(typeof(WirelessCommunicator), test.GetType());
  10:         }
  11:     }
  12: }

Now we can run our unittest, and obviously it fails.

image

We need to create a binding rule class, and finish the factory class. To do that, we are also going to need another class, a class which will actually create an instance of an object. The reason for this is the following: When the Factory.Bind<TInterface>() method is called, an instance of the binding rule class is created. But the IBindingRule<TInterface>.To<TClass>() method specifies to which class-type the binding-rule should use. Therefore the binding rule cannot hold the class-type, and this is where the ‘instance creator’ comes into play. The instance creator class knows the interface-type, and the class-type, and therefore can return an object of the class-type.

I was struggling with the instance creator class at first, but once I figured out that the interface doesn’t know the class-type, and the class does know the class-type, things started to work out.

To reference the non-generic binding rules from the Factory a non-generic binding rule interface is necessary; the generic binding rule interface of course inherits this interface as well.

Lets create the binding rule class, the instance creator interface and class, and finish the factory class.

   1: namespace MyDI {
   2:     interface IInstanceCreator<TInterface> {
   3:         TInterface Create();
   4:     }
   5: }
   1: namespace MyDI {
   2:     class InstanceCreator<TInterface, TClass> : IInstanceCreator<TInterface>
   3:         where TClass : class, TInterface, new() {
   4:         public TInterface Create() {
   5:             return new TClass();
   6:         }
   7:     }
   8: }
   1: namespace MyDI {
   2:     public interface IBindingRule { }
   3:  
   4:     public interface IBindingRule<TInterface> : IBindingRule {
   5:         void To<TClass>() where TClass : class, TInterface, new();
   6:     }
   7: }
   1: namespace MyDI {
   2:     class BindingRule<TInterface> : IBindingRule<TInterface> {
   3:         private IInstanceCreator<TInterface> _creator;
   4:  
   5:         public void To<TClass>() where TClass : class, TInterface, new() {
   6:             _creator = new InstanceCreator<TInterface, TClass>();
   7:         }
   8:  
   9:         public TInterface Create() {
  10:             if (_creator != null) {
  11:                 return _creator.Create();
  12:             }
  13:             throw new InvalidOperationException();
  14:         }
  15:     }
  16: }
   1: namespace MyDI {
   2:     public static class Factory {
   3:         public static IBindingRule<TInterface> Bind<TInterface>() {
   4:             throw new KeyNotFoundException();
   5:             if (!_bindingRules.ContainsKey(typeof(TInterface))) {
   6:                 var bindingRule = new BindingRule<TInterface>();
   7:                 _bindingRules.Add(typeof(TInterface), bindingRule);
   8:                 return bindingRule;
   9:             }
  10:             throw new InvalidOperationException();
  11:         }
  12:  
  13:         private static readonly Dictionary<Type, IBindingRule> _bindingRules = new Dictionary<Type, IBindingRule>();
  14:  
  15:         public static TInterface GetInstance<TInterface>() {
  16:             if (_bindingRules.ContainsKey(typeof(TInterface)) && _bindingRules[typeof(TInterface)] is BindingRule<TInterface>) {
  17:                 return ((BindingRule<TInterface>)_bindingRules[typeof(TInterface)]).Create();
  18:             }
  19:             throw new InvalidOperationException();
  20:         }
  21:     }
  22: }

So what is exactly happening here?

Interface IInstanceCreator<TInterface> This interface defines an instance creator. Generics-wise it only knows the interface-type, since the binding rule must hold a reference to the instance creator. It has a single method, the Create() method, which is meant to return an instance of an object which implements the interface-type.

Class InstanceCreator<TInterface, TClass> This class implements the IInstanceCreator<TInterface> interface, and therefore has a Create() method. This method can simple create a new object of the class-type using the parameterless constructor.

Interface IBindingRule This interface defines a non-generic binding rule. This is necessary for the factory to reference a collection of binding rules. Of course it could be a list of objects as well, but using this interface it is somewhat strongly typed.

Interface IBindingRule<TInterface> This interface defines a generic binding rule. This interface exposes a single method to define the binding, specifying the class-type using generics.

Class BindingRule<TInterface> This class implements the IBindingRule<TInterface> interface. It is actually very simple. When the To<TClass> method is called, an instance of the InstanceCreator class is created. When the Create method is called, the instance creator is used to create an instance of the class-type.

Class Factory This class provides public access to the binding rules by exposing two static methods. One is the Bind<TInterface> method which can be used to create a binding rule. The binding rule is stored in a private dictionary. The other method is the GetInstance<TInterface> method in which a binding rule is retrieved from the dictionary, and the Create-method is used to get an instance of the object.

Now we have a unittest that passes. Excellent!

image

Using the factory we can define a binding rule in one assembly, and get an instance of a class that normally wouldn’t be accessible (without using reflection or some other method). The following diagram shows what I mean.

image

In the core assembly an interface is defined, and in that same assembly we want to use it. But the implementation is done in the implementing assembly which we can´t reference, since that assembly references the core assembly. This is possible using the factory.

Please note that the code shown above is not yet thread-safe.

In the next post I will introduce the concept of the binding scope. With the binding scope we can specify if and how previously created objects are re-used. For example: a singleton-scope.

The code can be downloaded here.