| | |
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:
="1.0"="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?