Click here to Skip to main content
Click here to Skip to main content
Go to top

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

, 15 Nov 2010
Rate this:
Please Sign up or sign in to vote.
How to use Spring.Net DI reducing coupling and enhancing automated testing.
This is an old version of the currently published article.
Previous Previous
Chapter IX Chapter XI

An appendix section has been added explaining how to get the client application running.

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 xlient 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.

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 we find 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 a Singleton, and the method to use for Spring.Net to create an instance of the class. We also indicate that the ContractManager property is set with the instance declared at ContractManagerRef:

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

NextAdapterLocator is another reference that needs to be injected; this is where we reference a server component for the first time.

As it can be seen above, this component does not need to reference any other service/component; unfortunately for us, this service implementation relies on 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"):

Project tidy-up

At this point, we can remove the references in the WPFClient:

We need some sort of mechanism so the server and Naive assemblies are deployed to the bin folders before we can execute the application. We are going to keep it simple and use the build events within the Visual Studio projects. The first step is to copy the assemblies to a common folder called eDirectory located in the libs folder (this is a good practice anyway); the second step is to copy the assemblies in that folder to the bin folder when the client is compiled. Below, we can see how the Naive project has been modified so it copies the assembly to the mentioned folder:

We don't want to copy these assemblies by default; only when we are running the InMemory client will we require them. So we are going to create a new configuration called "InMemory":

Now we can change the build events for the client to copy the assemblies if the project is compiled using the "In-Memory" configuration. The following script is executed:

IF $(ConfigurationName) == InMemory 
  copy "$(SolutionDir)..\libs\eDirectory\eDirectory.Domain.*" "$(TargetDir)" /Y
IF $(ConfigurationName) == InMemory 
  copy "$(SolutionDir)..\libs\eDirectory\eDirectory.Naive.*" "$(TargetDir)" /Y

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. Also, articulating DI in our tests makes it very easy to run the same tests against different service implementations, 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 references in the client application to 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 as we are writing code, a continuous integration environment could then execute the same tests using NHibernate and a database. Nice, isn't it?

Appendix - Set the In-Memory configuration

There are a few steps to follow to get the eDirectory.WPF client project working using the in-memory configuration:

  • Create a new custom configuration: InMemory
  • Add script to the project build events
  • Set the project dependencies

If you get the latest version at the Chapter X branch, the above steps are done for you. Following, we explain how to set the correct configuration before you execute the client application.

Set eDirectory.WPF as the 'StartUp Project' and then set the custom 'InMemory' configuration:

You just need to start the application; the server and Naive projects are compiled in first place, and the assemblies are copied to the libs folder. Once the client finishes the compilation, the build event scripts copies the server and Naive assemblies to the client bin folder. It is worth noting that although we don't have a reference to the server and Naive components, we can indicate that a dependency between the projects exist so they are built in the correct order; VS allows to modify this setting using the project dependency property:

License

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

Share

About the Author

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

Comments and Discussions


Discussions posted for the Published version of this article. Posting a message here will take you to the publicly available article in order to continue your conversation in public.
 
QuestionServerNHConfiguration.XML does not resolve! Pinmembergunantha15-Mar-14 10:35 
AnswerRe: ServerNHConfiguration.XML does not resolve! PinmemberEnrique Albert18-Mar-14 0:14 
Question"Error creating object with name 'ServerContainerRef'" PinmemberMember 94134637-Sep-12 5:43 
AnswerRe: "Error creating object with name 'ServerContainerRef'" PinmemberEnrique Albert7-Sep-12 12:05 
GeneralRe: "Error creating object with name 'ServerContainerRef'" [modified] PinmemberMember 941346310-Sep-12 5:41 
GeneralRe: "Error creating object with name 'ServerContainerRef'" PinmemberEnrique Albert19-Oct-12 8:33 
GeneralFew comments and suggestions PinmemberSTL731-Oct-10 4:21 
GeneralRe: Few comments and suggestions PinmemberEnrique Albert31-Oct-10 20:59 
GeneralRe: Few comments and suggestions PinmemberSTL731-Oct-10 23:58 
GeneralRe: Few comments and suggestions PinmemberEnrique Albert2-Nov-10 3:29 
GeneralRe: Few comments and suggestions PinmemberEnrique Albert14-Nov-10 2:14 
GeneralRe: Few comments and suggestions PinmemberSTL720-Nov-10 11:15 
GeneralMy vote of 5 Pinmemberlinhjob21-Oct-10 5:04 
GeneralRe: My vote of 5 PinmemberEnrique Albert28-Oct-10 3:02 
QuestionCan't Build Solution Pinmemberjmajic18-Oct-10 21:56 
AnswerRe: Can't Build Solution PinmemberEnrique Albert18-Oct-10 23:17 
GeneralRe: Can't Build Solution Pinmemberjmajic19-Oct-10 2:54 
GeneralRe: Can't Build Solution PinmemberEnrique Albert19-Oct-10 3:04 
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 

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
Web01 | 2.8.140916.1 | Last Updated 15 Nov 2010
Article Copyright 2010 by Enrique Albert
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid