Thursday, 12 November 2009

Unity Application Block

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.

Code Snippet
  1. namespace InterfaceLibrary
  2. {
  3.     public interface ICustomer
  4.     {
  5.         string FirstName{get;set;}
  6.         string Surname{get;set;}
  7.     }
  8. }

 

And has an interface for the ServiceLayer called IsvcCustomer

Code Snippet
  1. namespace InterfaceLibrary
  2. {
  3.     public interface IsvcCustomer
  4.     {
  5.         ICustomer GetCustomer();
  6.     }
  7. \

 

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.

Code Snippet
  1. using InterfaceLibrary;
  2. namespace BusinessObjects
  3. {
  4.     public class Customer : ICustomer
  5.     {
  6.         public string FirstName { get; set; }
  7.         public string Surname { get; set; }
  8.     }
  9. }

 

ServiceLayer

Again a class file which implements an Interface, with a simple piece of code returning a Customer object

Code Snippet
  1. using BusinessObjects;
  2. using InterfaceLibrary;
  3. namespace Service
  4. {
  5.     public class svcCustomer :IsvcCustomer
  6.     {
  7.         public ICustomer GetCustomer()
  8.         {
  9.             return new Customer(){FirstName = "Fred", Surname = "Bloggs"};
  10.         }
  11.     }
  12. \

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.

Code Snippet
  1. var container = new UnityContainer();

Then we Register the types

Code Snippet
  1. 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

Code Snippet
  1.             var myService = container.Resolve<IsvcCustomer>();
  2.             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;

  1. Create an ILogger interface and Logger class
  2. Create a new ServiceLayer class
  3. Amend the FormApplication so this new class is called.

ILogger

ILogger has one method, WriteLog which takes a string as a parameter

Code Snippet
  1. namespace InterfaceLibrary
  2. {
  3.     public interface ILogger
  4.     {
  5.         void WriteLog(string message);
  6.     }
  7. \

Logger

Logger implements WriteLog and takes the parameter and chucks it out to the output window.

Code Snippet
  1. using System;
  2. using System.Diagnostics;
  3. using InterfaceLibrary;
  4. namespace Service
  5. {
  6.     public class Logger : ILogger
  7.     {
  8.         public void WriteLog(string message)
  9.         {
  10.             Trace.WriteLine(string.Format("{0}: {1}",
  11.                                           DateTime.Now,
  12.                                           message));
  13.         }
  14.     }
  15. }

 

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.

Code Snippet
  1. using BusinessObjects;
  2. using InterfaceLibrary;
  3. namespace Service
  4. {
  5.     public class svcCustomer2 : IsvcCustomer
  6.     {
  7.         private ILogger _logger;
  8.         public svcCustomer2(ILogger logger)
  9.         {
  10.             _logger = logger;                          
  11.         }
  12.         public ICustomer GetCustomer()
  13.         {
  14.             _logger.WriteLog("Method Called");
  15.             return new Customer() { FirstName = "Jane", Surname = "Doe" };
  16.         }
  17.     }
  18. \

 

FormApplication

The only code change we need is to register the logger

Code Snippet
  1. container.RegisterType<ILogger, Logger>();

And then change the mapping to the new service

Code Snippet
  1. 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

Code Snippet
  1. svcCustomer2 svcCustomer2 = new svcCustomer2(new Logger());

which is clearly different to the svcCustomer, which does not have a constructor

Code Snippet
  1. 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