Click here to Skip to main content
Click here to Skip to main content

WCF by Example - Chapter X - Dependency Injection with Spring.Net

, 14 Oct 2011
Rate this:
Please Sign up or sign in to vote.
How to use Spring.Net DI reducing coupling and enhancing automated testing.
Previous Next
Chapter IX Chapter XI

The series

WCF by Example is a series of articles that describes how to design and develop a WPF client using WCF for communication and NHibernate for persistence purposes. The series introduction describes the scope of the articles, and discusses the architect solution at a high level. The source code for the series can be found at CodePlex.

Chapter overview

At this stage, the series has covered how we have put together the baseline for our application so we can start demonstrating functionality to our clients and gather feedback from our stakeholders and product owners. As we have explained in the previous chapters, the In-Memory implementation proves to be a valuable approach for RAD and TDD methodologies, postponing the development of other more expensive back-end infrastructure components for a later stage when the business domain is more stable.

In this chapter, we will explain how to leverage Dependency Injection in our design, providing the perfect mechanism for testing, developing, and business exploring. Currently, the client application has references to assemblies that we don't intent to deploy along with the client in a production environment. We are going to use Spring.Net in combination with the already introduced ServiceLocator pattern to remove these hard-coded references from the client; we will also use this mechanism in the test project. In a later chapter, we will demonstrate how this design facilitates the introduction of the persistence and communication components.

We will also spend some time introducing DI in our tests with the purpose of being able to execute our test cases using both the in-memory or NHibernate repositories without any changes in our test logic.

Review

Currently, we are executing the application in a single process where the client, server, and in-memory components are all tightly coupled as the client application has references to assemblies that will not be deployed along with the client in a production environment. The following diagram indicates at a high level the main components used when the in-memory client is executed:

We need to provide a mechanism so we can "inject" the above implementations but at the same time ensuring that the client project does not keep a reference to assemblies that will not be deployed along on the production environment.

To ease the build of the eDirectory application, we will leave the server and mock references in the client project, in a real solution, however, this type of references should not exist and deployment of them to the bin folder should be resolved by the build mechanism instead.

Spring.Net Container

We will use DI on both the client and server side so we will create a "holder" class named DiContext for the Spring.Net container in the common assembly:

public class DiContext
{
    public static XmlApplicationContext AppContext { get; set; }
}

As a result, we need to add two references to our assembly:

Re-factor the client

WCFClient contains a class to initialise dependencies, called eDirectoryBootStrapper; it is in here that the client indicates the implementation instances for the above mentioned components. We are going to replace this code by setting up the Spring container. From the App.config file, we will obtain the Spring file name and then use it to set the container. Below is the code before and after the re-factoring:

There are no more changes required at the client side; the only thing left is to set the App.config file and the Spring file. The App.config looks like:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="SpringConfigFile" value="file://InMemoryConfiguration.xml" />
  </appSettings>
</configuration>

In-Memory Spring configuration file

We are going to briefly discuss the Spring configuration file from our application perspective, covering the most common scenarios in declaring DI Spring.Net files. However, you might want to check the reference documentation which is quite good. A good practice when working with configuration files in VS is to set the schemas property to the Spring.Net schema definition file:

We need to configure ClientServiceLocator indicating that it is going to be a Singleton, and the method that Spring.Net needs to use to create an instance of the class. We also indicate that the CommandDispatcher property is set with the instance declared at CommandDispatcherRef:

It is worth noting that Spring.Net is capable of setting the reference for the CommandDispatcher property even if the setter is private; which is a nice mechanism to ensure services are only injected by the DI infrastructure. The CommandDispatcher is declared as follows:

We also need setting up the GlobalContext properly. If we were to run the application at this point without setting up the GlobalContext, the following exception would be thrown:

Let's define the Container in the server side:

The Container only exposes static properties, so we don't need a Factory-method; Spring.Net permits injecting static references like the RequestContext static property:

And the last thing to do is to declare the GlobalContext and the TransactionManagerFactory:

And now, the TransFactory reference needs to be added; in the InMemory mode, we use the Naive implementation (by the way, we are renaming these classes in this chapter so the suffix "EntityStore" has been replaced by "InMemory"):

Re-factor of tests

We need to re-factor the tests so DI is used when they are executed; as we indicated, some setters are private to avoid misuse of services; this proves to break the compilation of our tests. In the other hand, articulating DI in our tests makes it very easy to run the same tests against different service implementations without having to create different versions for different execution modes, which proves to be beneficial. We will see in later chapters how we can execute the same test against In-Memory and NHibernate repositories by just changing our DI configuration.

The best approach is to think that our tests are just another type of client; as a result, we need some sort of boot-strapper, similar to the one on the client side, that can be used to initialise the Spring.Net container and set up our services. In the test case, we will create a new class named TestBootStrapper that uses the AssemblyInitialize attribute:

Couple things to mention about using this approach; to get the method working properly, the class needs to be tagged with the TestClass attribute and the method needs to be static, accepting a TestContext parameter. This method is only executed once when the tests are run.

We need to add the same Spring.Net references that we used for the client and reference the Spring.Net configuration file. We are using a little trick to share the files across multiple projects; you might want to look at the project file to see how it is achieved. The App.config needs to be changed in the same manner that the client file was. The TestBootStrapper ends like:

If we recall, the service tests initialise the services in the constructor:

With the Spring.Net container, we can simplify the code to be just:

It does not look like a big deal, but the fact that our tests delegate to Spring.Net to set up the services proves to be very beneficial as we said before; from now on, we don't need to change more code if we need our tests to execute different implementations.

If we run the tests at this stage, the following results are retrieved:

The reason why the CheckFindAllNotication test is falling is due to the fact that the execution of our tests are impacting in other tests. The In-Memory repositories are not re-created when the next tests execute so the customer instances are kept in memory between the test executions; this is not good at all. To prove it, if we run the failed test alone, it passes; it is only when multiple tests are executed that the test fails. To resolve this problem, we need to create a base class for our tests that ensures that the in-memory repositories are reset at the end of a test execution.

[TestClass]
public abstract class eDirectoryTestBase
{
    [TestInitialize]
    public virtual void TestsInitialize()
    {
        
    }

    [TestCleanup]
    public virtual void TestCleanUp()
    {
        ResetLocator();
    }

    private static void ResetLocator()
    {            
        using (ITransManager manager = 
               GlobalContext.Instance().TransFactory.CreateManager())
        {
            manager.ExecuteCommand(locator =>
                                       {
                                           var resetable = locator as IResetable;
                                           if (resetable == null) return null;
                                           resetable.Reset();
                                           return new DtoResponse();
                                       });
        }
    }
}

The code is simple; when the test finishes, it invokes the ResetLocator method which calls the Reset method in the Locator; the in-memory implementation implements this interface:

The implementation is straightforward; it just discards the old repository. The only thing left is to ensure that our test inherits from the new base class:

If the tests are executed once more, we can see that all of them pass:

Chapter summary

In this chapter, we saw how we eliminated the hard coded references in the client application to the Server and Naive components using Spring.Net DI; we also spent some time re-factoring the tests so they also use DI in the same fashion. We have demonstrated how DI enhances our code and provides a sleek mechanism when using different implementations.

In the next two chapters, we will introduce the NHibernate and WCF components; we will see how DI facilitates a transparent implementation between the new components and those that we have already covered in the previous chapters.

The next chapter covers the implementation of NHibernate repositories and explains how we can easily switch our tests to run against a database without changes in our tests; this approach proves to be very beneficial as we can quickly run our test in-memory when we are writing code, a continuous integration environment could then execute the same tests using NHibernate against a database. Nice, isn't it?

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Enrique Albert
Software Developer (Senior)
Ireland Ireland
No Biography provided
Follow on   Twitter

Comments and Discussions

 
GeneralWCF by Example Pinmembervisalia16-Oct-10 6:34 
GeneralRe: WCF by Example PinmemberEnrique Albert16-Oct-10 13:12 
GeneralRe: WCF by Example PinmemberMember 195080915-Jan-11 6:59 
GeneralRe: WCF by Example PinmemberEnrique Albert15-Jan-11 15:22 
GeneralRe: WCF by Example PinmemberMember 195080916-Jan-11 0:59 
GeneralRe: WCF by Example PinmemberEnrique Albert16-Jan-11 2:36 
GeneralRe: WCF by Example PinmemberMember 195080916-Jan-11 3:47 
GeneralRe: WCF by Example PinmemberEnrique Albert16-Jan-11 4:29 
GeneralRe: WCF by Example [modified] PinmemberMember 195080916-Jan-11 5:25 
GeneralRe: WCF by Example PinmemberMember 195080918-Jan-11 5:52 
GeneralRe: WCF by Example PinmemberEnrique Albert23-Jan-11 17:59 
GeneralRe: WCF by Example PinmemberEnrique Albert25-Jan-11 3:48 
GeneralRe: WCF by Example PinmemberEnrique Albert16-Oct-10 18:04 
GeneralRe: WCF by Example Pinmembervisalia17-Oct-10 3:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140709.1 | Last Updated 14 Oct 2011
Article Copyright 2010 by Enrique Albert
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid