This is post #7 in the Building Neno series. Please click here for a description of the Building Neno project and instructions on how to access the source code for this post.
Before I can build out my repositories the way I want to, I need to add an Inversion of Control (IOC) container. The only one I've ever used is Spring. While it's great for automatic dependency injection, it can be unwieldy to use in code for manual dependency injection, like this...
ContextRegistry.GetContext().ConfigureObject(postService, "PostService");
And for service location, like this...
var postService = (IPostService)ContextRegistry.GetContext().GetObject("PostService");
The result has to be cast, and objects have to be identified by string. Painful -- but the configured automatic dependency injection behind the scenes works like a charm. When I retrieve a service, it automatically has its repositories inserted, the repositories have their context and session objects inserted, and the context objects have their session object inserted too, all with the right scope. So I haven't been tempted just yet to find another container.
The solution for me is to build a simple container with the interface I want that farms out the heavy lifting to Spring. By using the provider pattern I can also decouple Spring from my framework, and make it possible to swap Spring for another container if I ever need to.
When I first implemented this on a personal project last year I was not aware of the Common Service Locator project, which would have been a year old at the time. The Common Service Locator project does something very similar and already has implementations for all the major IOC containers, including Spring.
The first thing to do is define the interface I want my container providers to fulfil. The plan is to expose the same methods through a static Container
class which will pass off to the container provider.
namespace StackingCode.Moja.InversionOfControl
{
public interface IContainerProvider
{
void Configure<T>(T target);
void Configure<T>(T target, string name);
T Get<T>();
T Get<T>(string name);
T Get<T>(object[] args);
T Get<T>(string name, object[] args);
}
}
The big improvements in this interface over Spring is the use of generics. I want to be able to retrieve objects by type -- and if there's only one object in the container with that type, I don't want to have to specify a name. And no more casting!
This will depend on the Spring assemblies so I'll make a new project to keep Moja light, StackingCode.Moja.InversionOfControl.Spring
.
Right, Spring has methods for finding objects by type, so this provider will be pretty easy to build.
namespace StackingCode.Moja.InversionOfControl.Spring
{
public class ContainerProvider : IContainerProvider
{
public ContainerProvider()
{
ApplicationContext = ContextRegistry.GetContext();
}
private IApplicationContext ApplicationContext { get; set; }
#region IContainerProvider Members
public void Configure<T>(T target)
{
ApplicationContext.ConfigureObject(target, GetObjectNameFor<T>());
}
// ...
public T Get<T>()
{
return (T)ApplicationContext.GetObject(GetObjectNameFor<T>());
}
// ...
#endregion
private string GetObjectNameFor<T>()
{
string[] names = ApplicationContext.GetObjectNamesForType(typeof(T));
if (names.Length < 1)
throw new ApplicationException(string.Format("{0} is not defined in Spring objects.", typeof(T).Name));
if (names.Length > 1)
throw new ApplicationException(string.Format("{0} matches more than one object defined in Spring objects.", typeof(T).Name));
return names[0];
}
}
}
As mentioned before, my Container
class is a static class with static methods matching the interface above. The static constructor uses a custom configuration section to find the type of the container provider it should be using -- which it instantiates and hangs on to.
namespace StackingCode.Moja.InversionOfControl
{
public static class Container
{
private const string CONFIG_SECTION = "inversionOfControl";
static Container()
{
var configSection = (InversionOfControlSection)ConfigurationManager.GetSection(CONFIG_SECTION);
if (configSection == null)
throw new ConfigurationErrorsException(string.Format("The {0} config section cannot be found.", CONFIG_SECTION));
Type providerType = Type.GetType(configSection.ContainerProvider.Type);
if (providerType == null)
throw new ConfigurationErrorsException("The Inversion of Control Container Provider Type cannot be found.", configSection.ContainerProvider.ElementInformation.Source, configSection.ContainerProvider.ElementInformation.LineNumber);
if (!typeof(IContainerProvider).IsAssignableFrom(providerType))
throw new ConfigurationErrorsException("The Inversion of Control Container Provider Type does not implement IContainerProvider.", configSection.ContainerProvider.ElementInformation.Source, configSection.ContainerProvider.ElementInformation.LineNumber);
Provider = (IContainerProvider)Activator.CreateInstance(providerType);
}
private static IContainerProvider Provider { get; set; }
public static void Configure<T>(T target)
{
Provider.Configure(target);
}
// ...
public static T Get<T>()
{
return Provider.Get<T>();
}
// ... and so on
}
}
Elegance in manual dependency injection...
Container.Configure(postService);
And service location...
var postService = Container.Get<IPostService>();
There are 0 comments.
browse with Pivot
Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue
Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)
Comments
Leave a Comment
Please register or login to leave a comment.