Introduction
The Unity Application Block is a lightweight framework which allows/simplifies the implementation of;
· Inversion of control (IoC)
· Service Location
· Dependency injection
See the terminology section for a brief discussion of these concepts.
When to use Unity
these have been compiled from http://msdn.microsoft.com/en-us/library/dd203206.aspx
- Your objects and classes may have dependencies on other objects or classes.
- Your dependencies are complex or require abstraction.
- You want to take advantage of constructor, method, or property call injection features.
- You want to manage the lifetime of object instances.
- You want to be able to configure and change dependencies at run time.
Disadvantages
- There is a slight performance hit.
- A lot more class files, interfaces etc are created.
- Code can be complicated to follow, and sometimes things “just happen” which can be confusing
- Junior developers need to understand new concepts.
Getting Unity
Unity’s MSDN page can be found at http://msdn.microsoft.com/en-us/library/dd203104.aspx and this example uses version 1.2
There is also a codeplex site at http://www.codeplex.com/wikipage?ProjectName=unity here sample projects and discussions can be found
Code Example
The following code example allows us to switch between two Customer Service Classes, one with logging one without. I appreciate this is not a realistic example but it does explain the point, it could easily be one uses an SQL Server database the other an Oracle.
I have 4 projects;
- InterfaceLibrary, this contains all of my interfaces.
- BusinessObjects, this contains my class file of the Customer Objects which implements the interface
- ServiceLayer, this populates and returns a BusinessObject
- FormApplication, this displays the information from the ServiceLayer.
IntefaceLibrary
This has interfaces for the Customer Object, which I call ICustomer.
- namespace InterfaceLibrary
- {
- public interface ICustomer
- {
- string FirstName{get;set;}
- string Surname{get;set;}
- }
- }
And has an interface for the ServiceLayer called IsvcCustomer
- namespace InterfaceLibrary
- {
- public interface IsvcCustomer
- {
- ICustomer GetCustomer();
- }
- \
As you can see neither of these are particularly exciting, you will notice that the GetCustomer method does not return a concrete type but rather the interface.
BusinessObjects
The BusinessObjects has a class which implements the ICustomer interface.
- using InterfaceLibrary;
- namespace BusinessObjects
- {
- public class Customer : ICustomer
- {
- public string FirstName { get; set; }
- public string Surname { get; set; }
- }
- }
ServiceLayer
Again a class file which implements an Interface, with a simple piece of code returning a Customer object
- using BusinessObjects;
- using InterfaceLibrary;
- namespace Service
- {
- public class svcCustomer :IsvcCustomer
- {
- public ICustomer GetCustomer()
- {
- return new Customer(){FirstName = "Fred", Surname = "Bloggs"};
- }
- }
- \
FormApplication
You will notice that so far there is no unity, this is bog standard if long winded code. On the form project is where unity starts to come in.
I have designed a form with a button and 2 labels, the button will call the service from unity and then change the text property of the labels with the object properties (FirstName and Surname).
Once we have the form we need to configure Unity.
To do so we get an instance of the unity container.
- var container = new UnityContainer();
Then we Register the types
- container.RegisterType<IsvcCustomer, svcCustomer>();
With this code we are saying that when we ask for IsvcCustomer use svcCustomer in it’s place, it is possible (and probably preferable) to store this mapping in either a Web.Config or App.Config.
To return a customer object we simply use
- var myService = container.Resolve<IsvcCustomer>();
- var customer = myService.GetCustomer();
container.Resolver returns an instance of svcCustomer.
What is important to note is that at no point do we have a reference to the concrete objects; Customer or svcCustomer. So as long as we keep the interface we can change these objects.
Changing svcCustomer
As mentioned earlier I now want to amend svcCustomer so it has some logging. Also at this point I am going to introduce Dependency Injection.
To the above project I need to make some the following changes;
- Create an ILogger interface and Logger class
- Create a new ServiceLayer class
- Amend the FormApplication so this new class is called.
ILogger
ILogger has one method, WriteLog which takes a string as a parameter
- namespace InterfaceLibrary
- {
- public interface ILogger
- {
- void WriteLog(string message);
- }
- \
Logger
Logger implements WriteLog and takes the parameter and chucks it out to the output window.
- using System;
- using System.Diagnostics;
- using InterfaceLibrary;
- namespace Service
- {
- public class Logger : ILogger
- {
- public void WriteLog(string message)
- {
- Trace.WriteLine(string.Format("{0}: {1}",
- DateTime.Now,
- message));
- }
- }
- }
svcCustomer2
svcCustomer2 implements the existing IscCustomer interface, which has not changed. However on the constructor we now need to pass in the logger class, in GetCustomer WriteLog is called.
- using BusinessObjects;
- using InterfaceLibrary;
- namespace Service
- {
- public class svcCustomer2 : IsvcCustomer
- {
- private ILogger _logger;
- public svcCustomer2(ILogger logger)
- {
- _logger = logger;
- }
- public ICustomer GetCustomer()
- {
- _logger.WriteLog("Method Called");
- return new Customer() { FirstName = "Jane", Surname = "Doe" };
- }
- }
- \
FormApplication
The only code change we need is to register the logger
- container.RegisterType<ILogger, Logger>();
And then change the mapping to the new service
- container.RegisterType<IsvcCustomer, svcCustomer2>();
Which can all be held in a configuration file, so no code changes.
Dependency Injection
Traditionally to instantiate svcCustomer2 we would need to use a piece of code similar to the one below
- svcCustomer2 svcCustomer2 = new svcCustomer2(new Logger());
which is clearly different to the svcCustomer, which does not have a constructor
- svcCustomer svcCustomer = new svcCustomer();
Unity is clever enough to realise that there is a dependency and because it understands the concept of ILogger and knows to use Logger it does so.
Terminology
Inversion of Control (IoC)
Inversion of Control is a design pattern (http://en.wikipedia.org/wiki/Inversion_of_control). The idea is when you tightly couple your business objects to your application you lose flexibility and opportunity for reuse.
Common implementations of IoC create an interface of your business objects. So the application defines what it needs, and the business objects can implement this interface in anyway. This means different business objects could be created for, say, different data sources. A configuration file will then tell the application which particular implementation to use.
Service Location
Service locator is a design pattern that allows us to define where the business object can be found. So for example it we can define the Service Locator to use a SQL Server based object, rather than an Oracle one.
Dependency Injection
This is a particular form of IoC where the control being “inverted” is the creation of the dependencies. So in a traditional application a Customer object maybe responsible for controlling the lifespan of it’s Address object. When Dependency Injection is used this control is handed over to the framework (in this case Unity). All the Customer object has is a method to get an instance of the Address object.
No comments:
Post a Comment