IoC containers are great at reducing complex dependencies in an application and the Dependency Injection design pattern has existed for quite some time. Although IoC containers are quite popular in the Java world but it’s taken some time for good IoC containers to emerge for .Net. Even MS has jumped on the bandwagon and is showing some love for IoC by giving us Unity as an IoC container.

Today there are many, many IoC containers to choose from. That’s the one thing I really like about the ALT.Net community, varied implementations and depth of choice that allows us to pick what implementation really fits our need. We have excellent IoC containers like Castle Windsor, StructureMap, NInject, Spring.Net, Unity… the list goes on and on. You can take a look at Scott Hanselman’s list of IoC containers available for .Net to get an idea of the depth of choice available: List of .NET Dependency Injection Containers (IOC)

I have more or less settled for Castle Windsor as my choice of IoC container for my projects and have used it quite a lot but recently I have been interested in stuff that Jeremy Miller has been doing to StructureMap. Some of the deep functionality added to StructureMap and the ability to do some really neat logging and debugging is very interesting. The fluent interface of StructureMap is also quite clean and something I would really like to use since I’m quite sick of XML (I know Windsor has a fluent interface and am aware of Binsdor, Aynde’s awesome DSL implementation for specifying Windsor container configuration. I cannot use Binsor quite yet in our projects because of external dependencies to Boo and although Windsor has a pretty good fluent interface I like the idea of the Registry in StructureMap)

I have been toying around with the preview release, 2.4.9, of StructureMap and have been itching to try it out. But then I realized that I need a way to insulate my application from the IoC container choice I make, well since the application shouldn’t really have to know the concrete IoC container that will be used. All the application should care about is getting a IoC container component which provides Dependency Injection semantics.

Hence the need for a wrapper that the application will use as a generic IoC container. The wrapper will encapsulate the IoC container of our choice, allowing us to swap out IoC container implementations if we need to. So in this post I’m going to provide an implementation of this wrapper… ingeniously called IoC :)

Some foundation work before we defining the IoC wrapper:

Okay, before I start on the IoC wrapper, I need some infrastructure work done. Ideally when application starts, the IoC container is created and all components are registered at that time. This IoC container instance then is stored for the life time of the application. If our project is a web site then the IoC container instance should be stored in the HttpApplication. Now I must admit the thought of this component came from Aynde’s implementation of LocalData in Rhino Commons (https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/rhino-commons/Rhino.Commons.Clr/LocalDataImpl/LocalData.cs), but the LocalData implementation doesn’t allow me to store information on the HttpApplication level and uses ThreadStatic to store thread speciffic data.

That won’t work for me since I want to use something similar to LocalData to store the IoC container instance on either the application level, i.e. App Domain level if it’s a client app or the HttpApplication if its a web site, or at the thread / pre-request level. To tackle this scenario I started out by first implementing a Storage class:

   13     public abstract class Storage

   14     {

   15         #region methods

   16         /// <summary>

   17         /// Gets the data stored with the specified key in the storage

   18         /// </summary>

   19         /// <typeparam name="T">The type of data to get.</typeparam>

   20         /// <param name="key">object. The key that uniquely identifies the data to retrieve.</param>

   21         /// <returns>A <typeparamref name="T"/> instance or null if not found.</returns>

   22         public T Get<T>(object key)

   23         {

   24             throw new NotImplementedException();

   25         }

   26 

   27         /// <summary>

   28         /// Adds or updates the data specified with the key in the Storage

   29         /// </summary>

   30         /// <typeparam name="T">The type of data to add or update.</typeparam>

   31         /// <param name="key">object. The key that uniquely identifies the data being stored.</param>

   32         /// <param name="value"><typeparamref name="T"/>. The value to add or update.</param>

   33         public void Set<T>(object key, T value)

   34         {

   35             throw new NotImplementedException();

   36         }

   37 

   38         /// <summary>

   39         /// Checks whether the Storage contains data with the specified key.

   40         /// </summary>

   41         /// <param name="key">object. The unique key to check for.</param>

   42         /// <returns>True if data specified with the key was found, else false.</returns>

   43         public bool Contains(object key)

   44         {

   45             throw new NotImplementedException();

   46         }

   47 

   48         /// <summary>

   49         /// Removes the data specified with the key in Storage

   50         /// </summary>

   51         /// <param name="key"></param>

   52         /// <returns></returns>

   53         public void Remove(object key)

   54         {

   55             throw new NotImplementedException();

   56         }

   57 

   58         /// <summary>

   59         /// Removes all data in Storage

   60         /// </summary>

   61         public void Clear()

   62         {

   63             throw new NotImplementedException();

   64         }

   65         #endregion

   66     }

So the Storage class is an abstract class with standard methods for adding, removing and clearing items from the storage. The idea is that the client will request the type of storage that it wants to use and then use the above methods to manipulate the storage. The following properties tell the Storage class what type of Storage instance is the client expecting to use:

   15         #region properties

   16         /// <summary>

   17         /// Gets whether the current application is a web application.

   18         /// </summary>

   19         /// <value>bool. True if the current application is a web application.</value>

   20         public static bool IsWebApplication

   21         {

   22             get { return System.Web.HttpContext.Current != null; }

   23         }

   24 

   25         /// <summary>

   26         /// Gets a <see cref="Storage"/> implementation that can be used to store application

   27         /// sepcific data for the current thread / request.

   28         /// </summary>

   29         public static Storage Local

   30         {

   31             get

   32             {

   33                 return null;

   34             }

   35         }

   36 

   37         /// <summary>

   38         /// Gets a <see cref="Storage"/> instance that can be used to store application specific

   39         /// data for the applicatuion.

   40         /// </summary>

   41         public static Storage Application

   42         {

   43             get

   44             {

   45                 return null;

   46             }

   47         }

   48 

   49         /// <summary>

   50         /// Gets a <see cref="Storage"/> instance that can be used to store application sepcific

   51         /// data in the current session.

   52         /// </summary>

   53         public static Storage Session

   54         {

   55             get

   56             {

   57                 return null;

   58             }

   59         }

   60         #endregion

For now the properties return null and I have added a IsWebApplication properties that would allow consumers to and implementations of Storage to know the current application is a web site.

Now, the way I have implemented the Add, Remove, Clear methods on Storage is to add a abstract GetInternalHashtable method that will be implemented by specific implementations of the Storage class, in this case Application, Local and Session, and then the Hashtable returned by this method will then be manipulated in the Add, Remove, Clear methods. Below is the implementation of the methods:

  109  #region methods

  110         /// <summary>

  111         /// Gets the data stored with the specified key in <see cref="Storage"/>.

  112         /// </summary>

  113         /// <typeparam name="T">The type of data to get.</typeparam>

  114         /// <param name="key">object. The key that uniquely identifies the data to retrieve.</param>

  115         /// <returns>A <typeparamref name="T"/> instance or null if not found.</returns>

  116         public T Get<T>(object key)

  117         {

  118             if (UseLocking)

  119             {

  120                 lock (LockInstance)

  121                     return (T)GetInternalHashtable()[key];

  122             }

  123             return (T)GetInternalHashtable()[key];

  124         }

  125 

  126         /// <summary>

  127         /// Adds or updates the data specified with the key in <see cref="Storage"/>.

  128         /// </summary>

  129         /// <typeparam name="T">The type of data to add or update.</typeparam>

  130         /// <param name="key">object. The key that uniquely identifies the data being stored.</param>

  131         /// <param name="value"><typeparamref name="T"/>. The value to add or update.</param>

  132         public void Set<T>(object key, T value)

  133         {

  134             if (UseLocking)

  135             {

  136                 lock (LockInstance)

  137                     GetInternalHashtable()[key] = value;

  138             }

  139             else

  140                 GetInternalHashtable()[key] = value;

  141         }

  142 

  143         /// <summary>

  144         /// Checks whether the <see cref="AppStorage"/> contains data with the specified key.

  145         /// </summary>

  146         /// <param name="key">object. The unique key to check for.</param>

  147         /// <returns>True if data specified with the key was found, else false.</returns>

  148         public bool Contains(object key)

  149         {

  150             if (UseLocking)

  151             {

  152                 lock (LockInstance)

  153                     return GetInternalHashtable().ContainsKey(key);

  154             }

  155             return GetInternalHashtable().ContainsKey(key);

  156         }

  157 

  158         /// <summary>

  159         /// Removes the data specified with the key in <see cref="Storage"/>.

  160         /// </summary>

  161         /// <param name="key"></param>

  162         /// <returns></returns>

  163         public void Remove(object key)

  164         {

  165             if (UseLocking)

  166             {

  167                 lock (LockInstance)

  168                     GetInternalHashtable().Remove(key);

  169             }

  170             else

  171                 GetInternalHashtable().Remove(key);

  172         }

  173 

  174         /// <summary>

  175         /// Removes all data in <see cref="Storage"/>.

  176         /// </summary>

  177         public void Clear()

  178         {

  179             if (UseLocking)

  180             {

  181                 lock (LockInstance)

  182                     GetInternalHashtable().Clear();

  183             }

  184             else

  185                 GetInternalHashtable().Clear();

  186         }

  187         #endregion

  188 

  189         #region abstract members

  190         /// <summary>

  191         /// When overriden, tells whether the <see cref="Storage"/> uses locking when retrieving and setting values.

  192         /// </summary>

  193         protected abstract bool UseLocking { get; }

  194 

  195         /// <summary>

  196         /// Gets the object used by the <see cref="Storage"/> for locking when retrieving and setting values.

  197         /// </summary>

  198         protected abstract object LockInstance { get; }

  199 

  200         ///<summary>

  201         /// When overriden by a sub class, provides the internal Hashtable used to store and retrieve

  202         /// data.

  203         ///</summary>

  204         ///<returns></returns>

  205         protected abstract Hashtable GetInternalHashtable();

  206         #endregion

So the above methods first ask the implementation whether locking is required for thread synchronization, which would be required if you are manipulating at the application scope, i.e. App Domain or HttpApplication. If locking is required then it asks the implementation to provide a object that Storage can use for locking and then uses the Hashtable returned by the GetInternalHashtable method.

Now to bring Storage full circle the next step is to provide specific implementations of Storage that will be returned in Local, Application and Session properties. Below is the complete code for the Storage class and it’s implementation (Also available in Rhinestone’s source code).

Storage.cs

   25     public abstract class Storage

   26     {

   27         #region fields

   28         private static AppStorage _appStorage;

   29         protected static readonly object AppStorageLock = new object();

   30 

   31         private static LocalStorage _localStorage;

   32         protected static readonly object LocalStorageLock = new object();

   33 

   34         private static SessionStorage _sessionStorage;

   35         protected static readonly object SessionStorageLock = new object();

   36         #endregion

   37 

   38         #region properties

   39         /// <summary>

   40         /// Gets whether the current application is a web application.

   41         /// </summary>

   42         /// <value>bool. True if the current application is a web application.</value>

   43         public static bool IsWebApplication

   44         {

   45             get { return System.Web.HttpContext.Current != null; }

   46         }

   47 

   48         /// <summary>

   49         /// Gets a <see cref="Storage"/> implementation that can be used to store application

   50         /// sepcific data for the current thread / request.

   51         /// </summary>

   52         public static Storage Local

   53         {

   54             get

   55             {

   56                 if (_localStorage == null)

   57                 {

   58                     lock (LocalStorageLock)

   59                     {

   60                         if (_localStorage == null)

   61                             _localStorage = new LocalStorage();

   62                     }

   63                 }

   64                 return _localStorage;

   65             }

   66         }

   67 

   68         /// <summary>

   69         /// Gets a <see cref="Storage"/> instance that can be used to store application specific

   70         /// data for the applicatuion.

   71         /// </summary>

   72         public static Storage Application

   73         {

   74             get

   75             {

   76                 if (_appStorage == null)

   77                 {

   78                     lock (AppStorageLock)

   79                     {

   80                         if (_appStorage == null)

   81                             _appStorage = new AppStorage();

   82                     }

   83                 }

   84                 return _appStorage;

   85             }

   86         }

   87 

   88         /// <summary>

   89         /// Gets a <see cref="Storage"/> instance that can be used to store application sepcific

   90         /// data in the current session.

   91         /// </summary>

   92         public static Storage Session

   93         {

   94             get

   95             {

   96                 if (_sessionStorage == null)

   97                 {

   98                     lock (SessionStorageLock)

   99                     {

  100                         if (_sessionStorage == null)

  101                             _sessionStorage = new SessionStorage();

  102                     }

  103                 }

  104                 return _sessionStorage;

  105             }

  106         }

  107         #endregion

  108 

  109         #region methods

  110         /// <summary>

  111         /// Gets the data stored with the specified key in <see cref="Storage"/>.

  112         /// </summary>

  113         /// <typeparam name="T">The type of data to get.</typeparam>

  114         /// <param name="key">object. The key that uniquely identifies the data to retrieve.</param>

  115         /// <returns>A <typeparamref name="T"/> instance or null if not found.</returns>

  116         public T Get<T>(object key)

  117         {

  118             if (UseLocking)

  119             {

  120                 lock (LockInstance)

  121                     return (T)GetInternalHashtable()[key];

  122             }

  123             return (T)GetInternalHashtable()[key];

  124         }

  125 

  126         /// <summary>

  127         /// Adds or updates the data specified with the key in <see cref="Storage"/>.

  128         /// </summary>

  129         /// <typeparam name="T">The type of data to add or update.</typeparam>

  130         /// <param name="key">object. The key that uniquely identifies the data being stored.</param>

  131         /// <param name="value"><typeparamref name="T"/>. The value to add or update.</param>

  132         public void Set<T>(object key, T value)

  133         {

  134             if (UseLocking)

  135             {

  136                 lock (LockInstance)

  137                     GetInternalHashtable()[key] = value;

  138             }

  139             else

  140                 GetInternalHashtable()[key] = value;

  141         }

  142 

  143         /// <summary>

  144         /// Checks whether the <see cref="AppStorage"/> contains data with the specified key.

  145         /// </summary>

  146         /// <param name="key">object. The unique key to check for.</param>

  147         /// <returns>True if data specified with the key was found, else false.</returns>

  148         public bool Contains(object key)

  149         {

  150             if (UseLocking)

  151             {

  152                 lock (LockInstance)

  153                     return GetInternalHashtable().ContainsKey(key);

  154             }

  155             return GetInternalHashtable().ContainsKey(key);

  156         }

  157 

  158         /// <summary>

  159         /// Removes the data specified with the key in <see cref="Storage"/>.

  160         /// </summary>

  161         /// <param name="key"></param>

  162         /// <returns></returns>

  163         public void Remove(object key)

  164         {

  165             if (UseLocking)

  166             {

  167                 lock (LockInstance)

  168                     GetInternalHashtable().Remove(key);

  169             }

  170             else

  171                 GetInternalHashtable().Remove(key);

  172         }

  173 

  174         /// <summary>

  175         /// Removes all data in <see cref="Storage"/>.

  176         /// </summary>

  177         public void Clear()

  178         {

  179             if (UseLocking)

  180             {

  181                 lock (LockInstance)

  182                     GetInternalHashtable().Clear();

  183             }

  184             else

  185                 GetInternalHashtable().Clear();

  186         }

  187         #endregion

  188 

  189         #region abstract members

  190         /// <summary>

  191         /// When overriden, tells whether the <see cref="Storage"/> uses locking when retrieving and setting values.

  192         /// </summary>

  193         protected abstract bool UseLocking { get; }

  194 

  195         /// <summary>

  196         /// Gets the object used by the <see cref="Storage"/> for locking when retrieving and setting values.

  197         /// </summary>

  198         protected abstract object LockInstance { get; }

  199 

  200         ///<summary>

  201         /// When overriden by a sub class, provides the internal Hashtable used to store and retrieve

  202         /// data.

  203         ///</summary>

  204         ///<returns></returns>

  205         protected abstract Hashtable GetInternalHashtable();

  206         #endregion

  207     }

AppStorage.cs

   22     public class AppStorage : Storage

   23     {

   24         #region fields

   25         private static Hashtable _internalStorage;

   26         #endregion

   27 

   28         #region properties

   29         /// <summary>

   30         /// Overriden. Configures the storage to use locking when getting and setting values.

   31         /// </summary>

   32         protected override bool UseLocking

   33         {

   34             get { return true; }

   35         }

   36 

   37         /// <summary>

   38         /// Gets the object used by the <see cref="Storage"/> for locking when retrieving and setting values.

   39         /// </summary>

   40         protected override object LockInstance

   41         {

   42             get { return AppStorageLock; }

   43         }

   44         #endregion

   45 

   46         #region methods

   47         ///<summary>

   48         /// Overriden. Gets the internal hash table that is used to store and retrieve application

   49         /// specific data.

   50         ///</summary>

   51         ///<returns>A <see cref="Hashtable"/> that is used to store application specific data.</returns>

   52         /// <remarks>

   53         /// This method implementation uses locking as multiple threads (or requests in the case of a web app) can call

   54         /// the GetInternalHashtable at the same time.

   55         /// </remarks>

   56         protected override Hashtable GetInternalHashtable()

   57         {

   58             if (IsWebApplication)

   59             {

   60                 //This code is executing under a WebSite. Use the Application context to retrieve the hash table.

   61                 Hashtable internalHashtable = System.Web.HttpContext.Current.Application[typeof(LocalStorage).FullName] as Hashtable;

   62                 if (internalHashtable == null)

   63                 {

   64                     lock(AppStorageLock)

   65                     {

   66                         internalHashtable = System.Web.HttpContext.Current.Application[typeof(LocalStorage).FullName] as Hashtable;

   67                         if (internalHashtable == null)

   68                             System.Web.HttpContext.Current.Application[typeof(LocalStorage).FullName] = internalHashtable = new Hashtable();   

   69                     }

   70                 }

   71                 return internalHashtable;

   72             }

   73 

   74             //The code is running under a normal windows application. Use the static property.

   75             if (_internalStorage == null)

   76             {

   77                 lock (SessionStorageLock)

   78                 {

   79                     if (_internalStorage == null)

   80                         _internalStorage = new Hashtable();

   81                 }

   82             }

   83             return _internalStorage;

   84         }

   85         #endregion

   86     }

LocalStorage.cs

   23     public class LocalStorage : Storage

   24     {

   25         #region fields

   26         [ThreadStatic]

   27         private static Hashtable _internalStorage;

   28         #endregion

   29 

   30         #region properties

   31         /// <summary>

   32         /// Overriden. Configures the storage to not use locking when getting and setting values.

   33         /// </summary>

   34         protected override bool UseLocking

   35         {

   36             get { return false; }

   37         }

   38 

   39         /// <summary>

   40         /// Gets the object used by the <see cref="Storage"/> for locking when retrieving and setting values.

   41         /// </summary>

   42         protected override object LockInstance

   43         {

   44             get { return null; }

   45         }

   46         #endregion

   47 

   48         #region methods

   49         /// <summary>

   50         /// Overriden. Gets the <see cref="Hashtable"/> used to store thread specific data.

   51         /// </summary>

   52         /// <returns>A <see cref="Hashtable"/> that can be used to store thread / request specific data.</returns>

   53         /// <remarks>

   54         /// No locking is used when initializing the Hashtables as only one request to GetInternalHashtable can be made at one time.

   55         /// This code block is thread-safe

   56         /// </remarks>

   57         protected override Hashtable GetInternalHashtable()

   58         {

   59             if (IsWebApplication)

   60             {

   61                 Hashtable internalStorage = System.Web.HttpContext.Current.Items[typeof(LocalStorage).FullName] as Hashtable;

   62                 if (internalStorage == null)

   63                     System.Web.HttpContext.Current.Items[typeof(LocalStorage).FullName] = internalStorage = new Hashtable();

   64                 return internalStorage;

   65             }

   66             else

   67                 return _internalStorage ?? (_internalStorage = new Hashtable());

   68         }

   69         #endregion

   70     }

SessionStorage.cs

   24     public class SessionStorage : Storage

   25     {

   26         #region properties

   27         /// <summary>

   28         /// Overriden. Configures the storage to use locking when getting and setting values.

   29         /// </summary>

   30         protected override bool UseLocking

   31         {

   32             get { return true; }

   33         }

   34 

   35         /// <summary>

   36         /// Gets the object used by the <see cref="Storage"/> for locking when retrieving and setting values.

   37         /// </summary>

   38         protected override object LockInstance

   39         {

   40             get { return SessionStorageLock; }

   41         }

   42         #endregion

   43 

   44         #region methods

   45         /// <summary>

   46         /// Overriden. Gets the <see cref="Hashtable"/> used to store thread specific data.

   47         /// </summary>

   48         /// <returns>A <see cref="Hashtable"/> that can be used to store Session specific data.</returns>

   49         /// <remarks>

   50         /// This code block uses locking to create the Hashtable as multiple requests can execute under the same session in

   51         /// the case AJAX calls.

   52         /// </remarks>

   53         protected override Hashtable GetInternalHashtable()

   54         {

   55             Guard.Against<InvalidOperationException>(!IsWebApplication || HttpContext.Current.Session == null,

   56                                                     "An ASP.Net session must be available when using Session storage. No ASP.Net session was found in the current context.");

   57 

   58             Hashtable internalStorage = HttpContext.Current.Session[typeof(SessionStorage).FullName] as Hashtable;

   59             if (internalStorage == null)

   60             {

   61                 lock(SessionStorageLock)

   62                 {

   63                     internalStorage = HttpContext.Current.Session[typeof(SessionStorage).FullName] as Hashtable;

   64                     if (internalStorage == null)

   65                         HttpContext.Current.Session[typeof(SessionStorage).FullName] = internalStorage = new Hashtable();

   66                 }

   67             }

   68             return internalStorage;

   69         }

   70         #endregion

   71     }


Building a IoC wrapper component:

Starting with an abstract class, I start out with first defining abstract methods on an abstract IoC class. These abstract methods are the most common IoC methods I use in my applications and largely comprize of resolving component dependencies:

   13     public abstract class IoC

   14     {

   15         /// <summary>

   16         /// When overriden, should initialize and configure the container with the optionally provided

   17         /// path to the configuration file path.

   18         /// </summary>

   19         /// <param name="configFilePath">string. An optional path to the external configuration file

   20         /// that should be used by the container to initialize itself.</param>

   21         protected abstract void Initialize(string configFilePath);

   22 

   23         /// <summary>

   24         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type.

   25         /// </summary>

   26         /// <typeparam name="T">The type of component to resolve or build.</typeparam>

   27         /// <returns>An instance of <typeparamref name="T"/>.</returns>

   28         public abstract T Resolve<T>();

   29 

   30         /// <summary>

   31         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and service name

   32         /// </summary>

   33         /// <typeparam name="T">The type of component to resolve or build.</typeparam>

   34         /// <param name="serviceName">string. The component key that the component is registered with.</param>

   35         /// <returns>An instance of <typeparamref name="T"/>.</returns>

   36         public abstract T Resolve<T>(string serviceName);

   37 

   38         /// <summary>

   39         /// When overriden, tries to resolve a component of the specified <paramref name="type"/>

   40         /// returning a new instance of the type <typeparam name="T"/>

   41         /// </summary>

   42         /// <param name="type">A <see cref="Type"/> instance representing the type to resolve.</param>

   43         /// <returns>object. The resolved component if it exists.</returns>

   44         public abstract T Resolve<T>(Type type);

   45 

   46         /// <summary>

   47         /// When overriden, tries to resolve a component of the specified <paramref name="type"/> and service name

   48         /// returning a new instance of the type <typeparam name="T"/>

   49         /// </summary>

   50         /// <param name="type">A <see cref="Type"/> instance representing the type to resolve.</param>

   51         /// <param name="serviceName">string. The component key that the component is registered with.</param>

   52         /// <returns>object. The resolved component if it exists.</returns>

   53         public abstract T Resolve<T>(Type type, string serviceName);

   54 

   55         /// <summary>

   56         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and

   57         /// returns null (or default value for value types) when the component is not found.

   58         /// </summary>

   59         /// <typeparam name="T">The type of component to resolve.</typeparam>

   60         /// <returns>An instance of <typeparamref name="T"/> or null if the compnent is not found.</returns>

   61         public abstract T TryResolve<T>();

   62 

   63         /// <summary>

   64         /// When overriden, tries to resolve a component of type <typeparam name="T"/> registered for the type

   65         /// <paramref name="type"/> and returns null (or default value for value types) when the component is not found.

   66         /// </summary>

   67         /// <param name="type">The registered component to resolve..</param>

   68         /// <returns>An instance of the component if found, else null.</returns>

   69         public abstract T TryResolve<T>(Type type);

   70 

   71         /// <summary>

   72         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and

   73         /// returns the default value provided when the component is not found.

   74         /// </summary>

   75         /// <typeparam name="T">The type of component to resolve.</typeparam>

   76         /// <param name="defaultValue">An optional default instance of <typeparamref name="T"/> that is

   77         /// returned in case the component could not be resolved.</param>

   78         /// <returns>An instance of <typeparamref name="T"/> type.</returns>

   79         public abstract T TryResolve<T>(T defaultValue);

   80 

   81         /// <summary>

   82         /// When overriden, tries to resolve a component of the specified <typeparam name="T"/> type registered

   83         /// with the <paramref name="type"/> and returns the default value provided when a component registeration

   84         /// is not provided.

   85         /// </summary>

   86         /// <param name="type">The <see cref="Type"/> that the component is registered with.</param>

   87         /// <param name="defaultValue">The default value to return in case the component is not registered.</param>

   88         /// <returns>An instance of <typeparamref name="T"/> type or the default value if the component is not found.</returns>

   89         public abstract T TryResolve<T>(Type type, T defaultValue);

   90 

   91         /// <summary>

   92         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> and

   93         /// service name type returning null (or default value for value types) when the component is not found.

   94         /// </summary>

   95         /// <typeparam name="T">The type of component to resolve.</typeparam>

   96         /// <param name="serviceName">string. The component key with which the component is registered.</param>

   97         /// <returns>An instance of <typeparamref name="T"/> type or null if the component is not found.</returns>

   98         public abstract T TryResolve<T>(string serviceName);

   99 

  100         /// <summary>

  101         /// When overriden, tries to resolve the component of the specified <typeparamref name="T"/> and

  102         /// service name type returning the default value when the component is not found.

  103         /// </summary>

  104         /// <typeparam name="T">The type of component to resolve.</typeparam>

  105         /// <param name="serviceName">string. The component key with which the compinent is registered.</param>

  106         /// <param name="defaultValue">The default value to return if not found.</param>

  107         /// <returns>An instance of <typeparamref name="T"/> instance.</returns>

  108         public abstract T TryResolve<T>(string serviceName, T defaultValue);

  109 

  110         /// <summary>

  111         /// When overriden, tries to resolve the component of the specified <typeparamref name="T"/> registered

  112         /// with the specified <paramref name="type"/> and service name. Returns the default value provided when

  113         /// the registered component is not found.

  114         /// </summary>

  115         /// <typeparam name="T">The type of component to resolve.</typeparam>

  116         /// <param name="type">The <see cref="Type"/> that the component is registered with.</param>

  117         /// <param name="serviceName">string. The component key with which the component is registered.</param>

  118         /// <param name="defaultValue">The default value to return in case the component is not registered.</param>

  119         /// <returns>An instance of <typeparamref name="T"/> type or the default value if the component is not found.</returns>

  120         public abstract T TryResolve<T>(Type type, string serviceName, T defaultValue);

  121     }

The Initialize method is supposed to allow the IoC implementation to initialize and register all components. Next I add a Container property to the IoC class to allow getting an actual implementation of the abstract IoC class:

   33         #region properties

   34         /// <summary>

   35         /// Gets the current IoC container in the current application instance

   36         /// or HttpApplication that used for resolving instances.

   37         /// </summary>

   38         public static IoC Container

   39         {

   40             get

   41             {

   42                 if (Storage.Application.Contains(CONTAINER_KEY))

   43                 {

   44                     lock (typeof(IoC))

   45                     {

   46                         if (!Storage.Application.Contains(CONTAINER_KEY))

   47                         {

   48                             LoadContainer();

   49                         }

   50                     }

   51                 }

   52                 return Storage.Application.Get<IoC>(CONTAINER_KEY);

   53             }

   54         }

   55         #endregion

So the Container property attempts to get the concrete implementation if IoC from the application scope storage, or if it doesn’t exist it then calls a LoadContainer() method to load a default container. I’ll get into LoadContainer() in a moment…

Apart from that, sometimes I would also like to specify the concrete IoC instance to use as the container, mainly for mocking and testing basically. So to allow setting the container I’ve added a SetContainer method:

   60         /// <summary>

   61         /// Sets the container that is used as the current application's <see cref="Container"/> instance.

   62         /// </summary>

   63         /// <param name="instance"></param>

   64         public static void SetContainer(IoC instance)

   65         {

   66             lock (typeof(IoC)) //Sync lock to ensure only one thread updates the application container.

   67             {

   68                 Storage.Application.Set(CONTAINER_KEY, instance);

   69             }

   70         }

So now coming back to LoadContainer. Thinking about how to load up the container that the application use for Dependency Injection, the approach I wanted to take is to allow the application to define the concrete instance using the application’s (or website) configuration file. So what LoadContainer would do is check the configuration to find the Type that should be created as the default IoC container. Of course this Type must inherit from the IoC class. To do this I’ve created a custom configuration section that will be used by the LoadContainer method:

   22     /// <summary>

   23     /// Provides configuration settings to configure and initialize the <see cref="IoC"/> component.

   24     /// </summary>

   25     public class IoCConfiguration : ConfigurationSection

   26     {

   27         /// <summary>

   28         /// Gets or sets the fully qualified type name of a class that inherits from

   29         /// the <see cref="IoC"/> abstract class.

   30         /// </summary>

   31         [ConfigurationProperty("type", DefaultValue = "", IsRequired = true)]

   32         public string Type

   33         {

   34             get { return base["type"] as string;}

   35             set

   36             {

   37                 Guard.Against<NullReferenceException>(string.IsNullOrEmpty(value), "Please specify a valid type that inherits from Vantage.Shared.IoC");

   38                 base["type"] = value;

   39             }

   40         }

   41 

   42         /// <summary>

   43         /// Gets or sets the name of the external file that should be used to initialize

   44         /// the <see cref="IoC"/> implmentation.

   45         /// </summary>

   46         [ConfigurationProperty("fileName", DefaultValue = "", IsRequired = false)]

   47         public string FileName

   48         {

   49             get { return base["fileName"] as string; }

   50             set { base["fileName"] = value; }

   51         }

   52     }

Nothing fancy here, just a simple class inheriting from ConfigurationSection and has two properties; Type that identifies the type of class that should be loaded and an optional FileName to point to an external configuration file that is passed as the filePath parameter of the Initialize method on the IoC class.

With the configuration section defined, here’s the LoadContainer method:

   74         /// <summary>

   75         /// Loads the implementation of IoC container that should be used

   76         /// by the application to resolve instances.

   77         /// </summary>

   78         private static void LoadContainer()

   79         {

   80             //Load the vantage.shared.ioc configuration section for the application.

   81             IoCConfiguration config = ConfigurationManager.GetSection("rhinestone.shared.ioc") as IoCConfiguration;

   82             Guard.Against<ConfigurationException>(config == null, "Configuration section rhinestone.shared.ioc not found. Failed to configure a valid IoC container.");

   83 

   84             //Check and load the IoC implementing type to be used as the IoC container.

   85             Type type = Type.GetType(config.Type);

   86             Guard.InheritsFrom<IoC>(type, "The specified type in rhinestone.shared.ioc does not inherit from Rhinestone.Shared.IoC. Failed to configure valid IoC container.");

   87 

   88             //Creating an instance of the container and setting that instance as the application's container.

   89             IoC container = (IoC) Activator.CreateInstance(type);

   90             container.Initialize(config.FileName);

   91             SetContainer(container);

   92         }

Here’s the full IoC class:

   22 /// <summary>

   23     /// The IoC abstract classes is implemented by IoC container providers

   24     /// that provide inversion of control and service lookup functionality.

   25     /// </summary>

   26     public abstract class IoC

   27     {

   28         #region fields

   29         //Key field used as a key to get the container from the ApplicationData

   30         private static readonly object CONTAINER_KEY = typeof (IoC);

   31         #endregion

   32 

   33         #region properties

   34         /// <summary>

   35         /// Gets the current IoC container in the current application instance

   36         /// or HttpApplication that used for resolving instances.

   37         /// </summary>

   38         public static IoC Container

   39         {

   40             get

   41             {

   42                 if (Storage.Application.Contains(CONTAINER_KEY))

   43                 {

   44                     lock (typeof(IoC))

   45                     {

   46                         if (!Storage.Application.Contains(CONTAINER_KEY))

   47                         {

   48                             LoadContainer();

   49                         }

   50                     }

   51                 }

   52                 return Storage.Application.Get<IoC>(CONTAINER_KEY);

   53             }

   54         }

   55         #endregion

   56 

   57         #region methods

   58 

   59         #region public

   60         /// <summary>

   61         /// Sets the container that is used as the current application's <see cref="Container"/> instance.

   62         /// </summary>

   63         /// <param name="instance"></param>

   64         public static void SetContainer(IoC instance)

   65         {

   66             lock (typeof(IoC)) //Sync lock to ensure only one thread updates the application container.

   67             {

   68                 Storage.Application.Set(CONTAINER_KEY, instance);

   69             }

   70         }

   71         #endregion

   72 

   73         #region private

   74         /// <summary>

   75         /// Loads the implementation of IoC container that should be used

   76         /// by the application to resolve instances.

   77         /// </summary>

   78         private static void LoadContainer()

   79         {

   80             //Load the vantage.shared.ioc configuration section for the application.

   81             IoCConfiguration config = ConfigurationManager.GetSection("rhinestone.shared.ioc") as IoCConfiguration;

   82             Guard.Against<ConfigurationException>(config == null, "Configuration section rhinestone.shared.ioc not found. Failed to configure a valid IoC container.");

   83 

   84             //Check and load the IoC implementing type to be used as the IoC container.

   85             Type type = Type.GetType(config.Type);

   86             Guard.InheritsFrom<IoC>(type, "The specified type in rhinestone.shared.ioc does not inherit from Rhinestone.Shared.IoC. Failed to configure valid IoC container.");

   87 

   88             //Creating an instance of the container and setting that instance as the application's container.

   89             IoC container = (IoC) Activator.CreateInstance(type);

   90             container.Initialize(config.FileName);

   91             SetContainer(container);

   92         }

   93         #endregion

   94 

   95         #region abstract

   96         /// <summary>

   97         /// When overriden, should initialize and configure the container with the optionally provided

   98         /// path to the configuration file path.

   99         /// </summary>

  100         /// <param name="configFilePath">string. An optional path to the external configuration file

  101         /// that should be used by the container to initialize itself.</param>

  102         protected abstract void Initialize(string configFilePath);

  103 

  104         /// <summary>

  105         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type.

  106         /// </summary>

  107         /// <typeparam name="T">The type of component to resolve or build.</typeparam>

  108         /// <returns>An instance of <typeparamref name="T"/>.</returns>

  109         public abstract T Resolve<T>();

  110 

  111         /// <summary>

  112         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and service name

  113         /// </summary>

  114         /// <typeparam name="T">The type of component to resolve or build.</typeparam>

  115         /// <param name="serviceName">string. The component key that the component is registered with.</param>

  116         /// <returns>An instance of <typeparamref name="T"/>.</returns>

  117         public abstract T Resolve<T>(string serviceName);

  118 

  119         /// <summary>

  120         /// When overriden, tries to resolve a component of the specified <paramref name="type"/>

  121         /// returning a new instance of the type <typeparam name="T"/>

  122         /// </summary>

  123         /// <param name="type">A <see cref="Type"/> instance representing the type to resolve.</param>

  124         /// <returns>object. The resolved component if it exists.</returns>

  125         public abstract T Resolve<T>(Type type);

  126 

  127         /// <summary>

  128         /// When overriden, tries to resolve a component of the specified <paramref name="type"/> and service name

  129         /// returning a new instance of the type <typeparam name="T"/>

  130         /// </summary>

  131         /// <param name="type">A <see cref="Type"/> instance representing the type to resolve.</param>

  132         /// <param name="serviceName">string. The component key that the component is registered with.</param>

  133         /// <returns>object. The resolved component if it exists.</returns>

  134         public abstract T Resolve<T>(Type type, string serviceName);

  135 

  136         /// <summary>

  137         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and

  138         /// returns null (or default value for value types) when the component is not found.

  139         /// </summary>

  140         /// <typeparam name="T">The type of component to resolve.</typeparam>

  141         /// <returns>An instance of <typeparamref name="T"/> or null if the compnent is not found.</returns>

  142         public abstract T TryResolve<T>();

  143 

  144         /// <summary>

  145         /// When overriden, tries to resolve a component of type <typeparam name="T"/> registered for the type

  146         /// <paramref name="type"/> and returns null (or default value for value types) when the component is not found.

  147         /// </summary>

  148         /// <param name="type">The registered component to resolve..</param>

  149         /// <returns>An instance of the component if found, else null.</returns>

  150         public abstract T TryResolve<T>(Type type);

  151 

  152         /// <summary>

  153         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> type and

  154         /// returns the default value provided when the component is not found.

  155         /// </summary>

  156         /// <typeparam name="T">The type of component to resolve.</typeparam>

  157         /// <param name="defaultValue">An optional default instance of <typeparamref name="T"/> that is

  158         /// returned in case the component could not be resolved.</param>

  159         /// <returns>An instance of <typeparamref name="T"/> type.</returns>

  160         public abstract T TryResolve<T>(T defaultValue);

  161 

  162         /// <summary>

  163         /// When overriden, tries to resolve a component of the specified <typeparam name="T"/> type registered

  164         /// with the <paramref name="type"/> and returns the default value provided when a component registeration

  165         /// is not provided.

  166         /// </summary>

  167         /// <param name="type">The <see cref="Type"/> that the component is registered with.</param>

  168         /// <param name="defaultValue">The default value to return in case the component is not registered.</param>

  169         /// <returns>An instance of <typeparamref name="T"/> type or the default value if the component is not found.</returns>

  170         public abstract T TryResolve<T>(Type type, T defaultValue);

  171 

  172         /// <summary>

  173         /// When overriden, tries to resolve a component of the specified <typeparamref name="T"/> and

  174         /// service name type returning null (or default value for value types) when the component is not found.

  175         /// </summary>

  176         /// <typeparam name="T">The type of component to resolve.</typeparam>

  177         /// <param name="serviceName">string. The component key with which the component is registered.</param>

  178         /// <returns>An instance of <typeparamref name="T"/> type or null if the component is not found.</returns>

  179         public abstract T TryResolve<T>(string serviceName);

  180 

  181         /// <summary>

  182         /// When overriden, tries to resolve the component of the specified <typeparamref name="T"/> and

  183         /// service name type returning the default value when the component is not found.

  184         /// </summary>

  185         /// <typeparam name="T">The type of component to resolve.</typeparam>

  186         /// <param name="serviceName">string. The component key with which the compinent is registered.</param>

  187         /// <param name="defaultValue">The default value to return if not found.</param>

  188         /// <returns>An instance of <typeparamref name="T"/> instance.</returns>

  189         public abstract T TryResolve<T>(string serviceName,T defaultValue);

  190 

  191         /// <summary>

  192         /// When overriden, tries to resolve the component of the specified <typeparamref name="T"/> registered

  193         /// with the specified <paramref name="type"/> and service name. Returns the default value provided when

  194         /// the registered component is not found.

  195         /// </summary>

  196         /// <typeparam name="T">The type of component to resolve.</typeparam>

  197         /// <param name="type">The <see cref="Type"/> that the component is registered with.</param>

  198         /// <param name="serviceName">string. The component key with which the component is registered.</param>

  199         /// <param name="defaultValue">The default value to return in case the component is not registered.</param>

  200         /// <returns>An instance of <typeparamref name="T"/> type or the default value if the component is not found.</returns>

  201         public abstract T TryResolve<T>(Type type, string serviceName, T defaultValue);

  202         #endregion

  203 

  204         #endregion

Providing an implementation for StructureMap:

Okay, so now that I have a IoC wrapper how would one use you might ask? So for you application or website or whatever… you could then create a subclass of IoC, set it up in the Initialize methods and then delegate the Resolve calls to the actual container of your choice. Here is one example using StructureMap, the following class can be used as a generic StructureMap container:

   15     public class StructureMapContainer : IoC

   16     {

   17         #region Overrides of IoC

   18         protected override void Initialize(string configFilePath)

   19         {

   20             //Initialize the object factory and setup the

   21             if (string.IsNullOrEmpty(configFilePath))

   22                 StructureMapConfiguration.ScanAssemblies();

   23             else

   24                 StructureMapConfiguration.IncludeConfigurationFromFile(configFilePath);

   25         }

   26 

   27         public override T Resolve<T>()

   28         {

   29             return ObjectFactory.GetInstance<T>();

   30         }

   31 

   32         public override T Resolve<T>(string serviceName)

   33         {

   34             return ObjectFactory.GetNamedInstance<T>(serviceName);

   35         }

   36 

   37         public override T Resolve<T>(Type type)

   38         {

   39             return (T) ObjectFactory.GetInstance(type);

   40         }

   41 

   42         public override T Resolve<T>(Type type, string serviceName)

   43         {

   44             return (T) ObjectFactory.GetNamedInstance(type, serviceName);

   45         }

   46 

   47         public override T TryResolve<T>()

   48         {

   49             return TryResolve(default(T));

   50         }

   51 

   52         public override T TryResolve<T>(Type type)

   53         {

   54             return TryResolve(type, default(T));

   55         }

   56 

   57         public override T TryResolve<T>(T defaultValue)

   58         {

   59             try

   60             {

   61                 object instance = ObjectFactory.GetInstance<T>();

   62                 return instance == null ? defaultValue : (T)instance;

   63             }

   64             catch (Exception)

   65             {

   66                 return defaultValue;

   67             }

   68         }

   69 

   70         public override T TryResolve<T>(Type type, T defaultValue)

   71         {

   72             try

   73             {

   74                 object instance = ObjectFactory.GetInstance(type);

   75                 return instance == null ? defaultValue : (T)instance;

   76             }

   77             catch (Exception)

   78             {

   79                 return defaultValue;

   80             }

   81         }

   82 

   83         public override T TryResolve<T>(string serviceName)

   84         {

   85             return TryResolve(serviceName, default(T));

   86         }

   87 

   88         public override T TryResolve<T>(string serviceName, T defaultValue)

   89         {

   90             try

   91             {

   92                 object instance = ObjectFactory.GetNamedInstance<T>(serviceName);

   93                 return instance == null ? defaultValue : (T)instance;

   94             }

   95             catch (Exception)

   96             {

   97                 return defaultValue;

   98             };

   99         }

  100 

  101         public override T TryResolve<T>(Type type, string serviceName, T defaultValue)

  102         {

  103             try

  104             {

  105                 object instance = ObjectFactory.GetNamedInstance(type, serviceName);

  106                 return instance == null ? defaultValue : (T)instance;

  107             }

  108             catch (Exception)

  109             {

  110                 return defaultValue;

  111             };

  112         }

  113         #endregion

  114     }

Using the StructureMapContainer now, I can either use a configuration file to define the registered plugin types or use the Registry class and define component registrations using that. But the bottom line is that my application isn’t really tied down to one implementation of an IoC container and I can swap out implementations quite easily since my application uses the IoC wrapper.

The complete source for the IoC wrapper can be found in Rhinestone’s source code: https://rhinestone.googlecode.com/svn/trunk/src/Rhinestone.Shared/IoC

Posted on Wednesday, August 27, 2008 11:27 AM | Filed Under [ Patterns IoC ]


Comments

Gravatar
# I cannot understand some parts of your code: Hash...
Posted by Anonymous
on 3/17/2011 12:47 PM
I cannot understand some parts of your code:

Hashtable internalHashtable = System.Web.HttpContext.Current.Application[typeof(LocalStorage).FullName] as Hashtable;

If my research is correct then the expression before ';as Hashtable'; yields an HttpApplicationState object which is NOT inherited directly or indirectly from Hashtable, so that ';as Hashtable'; should always yield null. Do I get something wrong? Could you explain the line, please?
Gravatar
# Hi Ritesh. Great blog. I've really been enjoying y...
Posted by Mike Hadlow
on 10/6/2008 8:33 AM
Hi Ritesh. Great blog. I've really been enjoying your IoC posts.<BR/><BR/>I don't want to steal your thunder, but have you checked out the Common Service Locator (IServiceLocator)?<BR/><BR/>www.codeplex.com/.../>It's been designed by the leading .NET IoC container developers. The idea is that any framework or application that needs to locate components should use it. It's a common standard for the whole .NET community to use, and it means that we don't need to repeat the same container abstraction over and over again.<BR/><BR/>Mike
Post Comment
Title *
Name *
Email
Url
Comment *  
Please add 3 and 7 and type the answer here: