Introducing Dependency Injection Frameworks

Published: 13 Apr 2007
By: Karl Seguin

Within a project, classes will inevitably rely on each other. In some situations, this coupling can make code difficult to unit test or dynamically extend. A common solution is to this problem is to use a Dependency Injection Pattern. In this article we'll look at how we can leverage the open-source Dependency Injection Framework called StructureMap, within The Code Wiki application.

To find out more about dependency injection and other hot DDD topics such as O/R Mapping, unit testing, mocking and system architecture, check out Karl's free ebook.

About The Code Wiki

The Code Wiki is an open source application meant to be used as a meaningful learning tool for programming practices such as domain driven design and unit testing. The Code Wiki also comes with a freely downloadable in-progress ebook with new chapters being released periodically. Some techniques, such as the use of Dependency Injection frameworks, were left out of The Code Wiki in order to avoid overwhelming readers. This article looks at how The Code Wiki could have made us of such a framework to help improve testability and allow for multiple database vendors to easily plug-into The Code Wiki. You can learn more by visiting http://www.thecodewiki.com/.

About Dependency Injection

Dependency Injection (DI) refers to the practice of injecting dependencies into your code. The common alternative to DI is to simply hard-code dependencies. DI is particularly well suited when the dependency in question provides a service, as opposed to simply having two domain classes interacting with each other (in other words, it probably wouldn't make sense to use a DI for the relationship between a User and a Role). The most obvious example is the dependency that a business layer has on the data access layer. If a User class has a Delete method which interacts directly with the data layer, you might simply do:

public void Delete
{
if (isNew)
{
throw new InvalidOperationException("...");
}
new DataStore().DeleteUser(_userId);
}
There are two significant problems with the above implementation. First, it's impossible to unit test without a functional implementation of the DataStore.DeleteUser function. Secondly, external vendors can't plug into your code to provide their own DataStore.

The Code Wiki uses a simple type of DI to solve the first problem. Without a more solid framework however, it doesn't support any type of extensibility. Dependencies on a service such as the data layer is particularly painful for unit testers which typically want to avoid having to create temporary databases with fake and controlled data. The problem is much worse for practitioners of Test Driven Development since no implementation of the dependency might exist yet. Once you understand the basics of a DI framework, implementing it within The Code Wiki will be a painless affair.

Constructor Injection

Dependency Injection solves both problems by turning the hard-coded DataStore instance into a more dynamic/pluggable implementation. The Code Wiki uses a simple Constructor Injector implementation, which, for the above code, would look like:

private IDataStore _dataStore;
//inject a IDataStore via the constructor public User(IDataStore dataStore)
{
//check for null and throw exception _dataStore = dataStore; } public void Delete
{
if (isNew)
{
throw new InvalidOperationException("...");
}
_dataStore.DeleteUser(_userId);
}

We've lessened the coupling by replacing the hard-coded instance with an interface which is passed into the constructor. The Code Wiki takes it a small step further and specifies a default IDataStore implementation if none is present (which makes the DI transparent to any consumers):

public User() : this(new DataStore()) {}

Why A Framework?

For The Code Wiki, constructor injection solved the main problem by allowing the injection of mock objects into troublesome couplings to make unit testing possible. However, I recently worked on a system where there were numerous "troublesome couplings," such as a CacheProvider, LogProvider and DataProvider. This quickly became hard to manage via constructor injection. Additionally, as we've already mentioned, constructor injection doesn't really solve the extensibility problem; how does someone add a new plug-in without recompiling the entire application?

StructureMap solves these problems by providing a configurable and queryable framework. StructureMap is lightweight enough to actually improve code readability. That said, the StructureMap homepage has a very wise message:

"Do not use StructureMap if an application or process requires little flexibility. The abstraction and indirection afforded by StructureMap is unnecessary and even harmful in simpler systems or processes."
You can learn more about StructureMap by visiting: http://structuremap.sourceforge.net/

StructureMap Example

Continuing with our UserDataStore example, our StructureMap example begins with the StructureMap.config configuration file (StructureMap supports configuration options other than XML, but XML tends to be the clearest from a learning point of view):

<StructureMap>
  <DefaultInstance
    PluginType="CodeBetter.Sample.IDataStore, CodeBetter.Sample"
    PluggedType="CodeBetter.Sample.DataStore, CodeBetter.Sample"
    ConnectionString="...." />
</StructureMap>

The configuration file defines the DataStore instance as the default instance for the IDataStore plugin. This is achieved using the new DefaultInstance element of StructureMap 2.0. In pure StructureMap lingo, we are defining a PluginFamily of type IDataStore with a default concrete plugin of type DataStore. We're also using StructureMap to pass a connection string into our constructor.

If we wanted to add a new "plugin", we could simply compile it into a separate assembly, drop it in the /bin folder and modify the StructureMap.config file.

Now that StructureMap is configured, our User class changes to:

private IDataStore _dataStore;
public User()
{
_dataStore = ObjectFactory.GetInstance<IDataStore>();
}

public void Delete
{
if (isNew)
{
throw new InvalidOperationException("...");
}
_dataStore.DeleteUser(_userId);
}

Rather than having to physically inject our dependency into the User class, we simply query StructureMap's ObjectFactory for the default instance. We can also use the GetNamedInstance method to load a plugin by it's key, rather than simply loading the default isntance.

As you might expect, we can also programmatically inject an instance into StructureMap, which is ideal for unit testing purposes:

ObjectFactory.InjectStub(typeof(IDataStore), new MockDataStore());
//undo the mock injection and reset to the configuration values ObjectFactory.ResetDefault();

Using StructureMap within The Code Wiki

With the basics of The Code Wiki, Depedency Injection and StructureMap behind us, we can now look at what changes to The Code Wiki are needed in order to leverage StructureMap. The steps we’ll go over took little more than 5 minutes to complete from start to end.

Of course, the first thing to do is add the reference to the StructureMap.dll inside the CodeBetter.TheCodeWiki project.

The Code Wiki’s StructureMap.config

With respect to this article, The Code Wiki is comprised of two core dependency layers – the domain entities rely on an IWikiRepository implementation which in turn relies on an IDataStore implementation. The StructureMap.config represents The Code Wiki’s actual dependencies:

<StructureMap>
  <DefaultInstance 
PluginType="CodeBetter.TheCodeWiki.IWikiRepository,
CodeBetter.TheCodeWiki"
PluggedType="CodeBetter.TheCodeWiki.WikiRepository,
CodeBetter.TheCodeWiki"
/> <PluginFamily
Type="CodeBetter.TheCodeWiki.DataStores.IDataStore"
Assembly="CodeBetter.TheCodeWiki" DefaultKey="SqlDataStore"> <Plugin
Type="CodeBetter.TheCodeWiki.DataStores.SqlDataStore" Assembly="CodeBetter.TheCodeWiki" ConcreteKey="SqlDataStore" /> </PluginFamily> </StructureMap>

We've used a DefaultInstance for our IWikiRepository plugin because it isn't foreseeable that we'll need to dynamically extend this functionality. However, notice that we've used the more flexible PluginFamily element for the IDataStore. This means that The Code Wiki could easily be made to run off of a different database, such as MySql or Oracle.

We add our StructureMap.config to the CodeBetter.TheCodeWiki project and, very importantly, change the Copy To Ouput Directory to Copy Always. This last step, which is also shown in the image below, makes sure the .config file is copied to our /bin folder.

StructureMap.Config Property Window

Changing The Code Wiki's Code

We must now replace our constructor injection code with code that uses StructureMap. The Document, Resource, CommentInformation and WikiRepository classes must all be tweaked.

Resource.cs changes from:

internal Resource() : this(new WikiRepository()) {}
internal Resource(IWikiRepository repository)
{
_repository = repository;
// validation setup }

To:

public Resource() : this(ObjectFactory.GetInstance<IWikiRepository>()){}
internal Resource(IWikiRepository repository)
{
_repository = repository;
// validation setup }

Also, we mustn't forget to change the factory methods CreateNewResource which must be changed in a very similar way. The changes to both the Document and CommentInformation classes almost identical to the changes above.

Interestingly, we could opt to remove the constructor injection altogether and change the Resource code to simply be:

public Resource()
{
_repository = ObjectFactory.GetInstance<IWikiRepository>();
// validation setup }
The above code might be necessary if we were dealing with multiple injections, but it's far less explicit as things would just seem to "magically" work.

Next we must modify the WikiRepository which depends on an IDataStore. Again our change is very similar to what we just did; however, StructureMap has a very neat auto-wiring feature which'll recursively push in dependencies into a constructor. What that means is that StructureMap will see that the WikiRepository constructor expects an IDataStore instance, and will recognize IDataStore as PluginFamily it is aware of an automatically push in our default plugin. By simply declaring our constructor as follows, StructureMap will automatically wire the right dependency - with no need to declare a default/parameterless constructor:
public WikiRepository(IDataStore dataStore)
{
_dataStore = dataStore;
}

Fixing our Unit Tests

Since we haven't changed our API, our unit tests, or any of our code for that matter, don't require any changes. We still have constructors/factories which allow us to manually inject our mock dependencies, which is a core requirement of unit testing. If we did remove these methods and relied on the much more implicit query technique, such as:

public Resource()
{
_repository = ObjectFactory.GetInstance<IWikiRepository>();
// validation setup }
StructureMap still allows us to easily inject our mock objects using the InjectStub and UnMock/Reset methods. We would first need to change the SetUp and TearDown methods of the dependency tests, which are aptly named XXXDepdencyTests. For example, the SetUp for DocumentDependencyTests would become:
[SetUp]
public void Setup()
{
_mock = new MockRepository();
_repository = _mock.CreateMock<IWikiRepository>();
ObjectFactory.InjectStub(typeof(IWikiRepository), _repository);
}
While the TearDown would be:
[TearDown]
public void Teardown()
{
_mock.VerifyAll();
ObjectFactory.ResetDefault();
}
We'd also have to remove the dependency injection (since that particular constructor overload would no longer exist):
public void GetVersionDoesntReturnNull()
{
Expect.Call(_repository.GetDocumentVersions(1)).Return(null);
_mock.ReplayAll();

Document document = new Document(_repository);
document.Id = 1;
Assert.AreEqual(0, document.GetVersions().Count);
}

Conclusion

In this article we looked at what Dependency Injection is and in which situations it might be a useful pattern for yout to apply. We also looked at StructureMap as a DI framework and explored how easy it would be to add it to an existing application, in this case, The Code Wiki.

To learn more visit the StructureMap homepage and The Code Wiki homepage.

About Karl Seguin

Karl Seguin is an senior application developer at Fuel Industries, located in Ottawa, Ontario. He's an editor here at DotNetSlackers, a blogger for the influential CodeBetter.com and a Microsoft MVP.

View complete profile

Top Articles in this category

Inversion of Control and Dependency Injection with Castle Windsor Container - Part I
Inversion of Control (IoC) and Dependency Injection (DI) are two related practices in software development which are known to lead to higher testability and maintainability of software products. While some people employ them daily in their work, many others still don't know much about them, mostly because they require in the former a shift in the usual thinking process.

How to Write a Provider Model
In this article Keyvan teaches you how to write your own data provider for .NET applications using the provider model.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part II
Since the previous article didn't show much of the features offered by Castle Windsor Container, I will resume the previous discussion extending the requirements of the former example to show how IoC deals with changes and what is its real potential.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part IV
In this article I introduce the missing core features I didn't tackle before.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part III
In the previous article I showed how to take advantage of some of the features offered by Windsor Container to configure components and supply dependencies. You have seen how to deal with compulsory and optional dependencies, as well as how to inject simple values and component references, either individually or collected into arrays, lists and dictionaries.

Top
 
 
 

Discussion


Subject Author Date
placeholder Well written! AndyNil ! 4/18/2007 9:47 PM
There's nothing about how to use StuctureMap Ostrovia Hi 7/21/2008 1:28 PM
placeholder RE: There's nothing about how to use StuctureMap Karl Seguin 7/21/2008 5:36 PM
Check it out Karl Seguin 7/21/2008 5:35 PM

Please login to rate or to leave a comment.

Product Spotlight