Logo
blank Skip to main content

Using Unity Inversion of Control in Legacy Code Refactoring

Projects, that have been written over a long period of time usually exhibit a number of problems, such as duplicate code and a wide use of anti-patterns. Code refactoring is complicated by the fact that system components are often closely interconnected.

It is a typical situation when changes, introduced to one part of such software, impact other, seemingly completely unrelated parts. This makes testing and bug fixing much harder. In this case, the most optimal solution from continuing support and development standpoint is to decouple and use Inversion of Control Container (IoC container) and Dependency Injection.

Another widespread legacy code issue is cross-cutting dependencies, such as logging and caching. This leads to the appearance of objects with virtually unlimited scope of use. Changes to cross-cutting functionality impact the whole system, which makes them unacceptably โ€œexpensiveโ€. The most widespread solution to this issue is to use aspect-oriented approach.

Legacy code refactoring and optimization are tasks we usually do providing our development services. This article will provide a brief Unity IoC tutorial and go into more detail about dependency injections and aspect-oriented programming. We hope to explain basic principles and provide you with a simple Microsoft Unity container tutorial with some examples in C# that will get you started with decoupling and help you solve your legacy code support issues.

Unity Inversion of Control (Unity IoC)

Inversion of Control (IoC) is a concept stating that aggregated system modules should not depend on implementation of subordinate modules. Instead, a separate module-container stores abstraction-implementation couple and manages the lifetime of these implementations.

One way to apply this approach is to use Service Locator. Designated component returns the required dependence when requested, therefore the client code should explicitly call Service Locator to get the dependence. This way we can use LazyLoad pattern for dependencies. This approach requires explicit changes to the clientโ€™s code.

Another way to implement this approach is to use Dependency Injection. In this case, dependencies are injected in a module via transferring to initializers, properties, constructors. Advantage of this approach is that client does not need to call the container, therefore it is completely decoupled from the source of the dependencies.  

Different approaches to implement IoC principle

In order to create objects, container uses the map of dependencies. Map of dependencies describes what kind of parameters should be transferred to constructor, what properties they need to be assigned to and what methods need to be called in order to inject dependencies in an object. After receiving request for an object of a certain type, container decides the type of an object that should be returned. Such map of dependencies is created for each type registered in IoC container. Apart from that, container holds the associations describing what type of the object needs to be returned for each requested identifier. Abstract type is often used as an identifier. Container creates another object for each dependency of a requested object, the newly created object also can have dependencies and this operation is then called recursively.

Ways to inject dependencies

Unity dependency injection can be performed using the following methods:

1. Using agreements

It supposes the absence of explicit configuring. In Unity, a simple agreement is used, that says that in every class there supposed to be a single constructor that gets all dependencies as parameters.

C#
public class HomeController : Controller
   {
       private readonly IWorker _worker = default(IWorker);
       public HomeController(IWorker worker)
       {
           _worker = worker;
       }
       public ActionResult Index()
       {
           return new ContentResult() { Content = _worker.DoWork().ToString() };
       }
   }

2. Setting dependencies via attributes

Properties have DependencyAttribute, a constructor has InjectionConstructorAttribute, and a method has InjectionMethodAttribute. DependencyAttribute can also be set for constructor parameters and injection methods.

While attaching DependencyAttribute to a property or a parameter, you can indicate the name of the dependency.

C#
 public class HomeController : Controller
   {
       private IWorker _worker = default(IWorker);
       [Dependency]
       public IWorker Worker { get { return _worker; } set { _worker = value; } }

       public ActionResult Index()
       {
           return new ContentResult() { Content = _worker.DoWork().ToString() };
       }
   }

3. Setting configuration in the code while adding an element to the container

While registering object in a container you can explicitly indicate dependencies. The last parameter of the RegisterType method is the InjectionMember array. This array can contain objects of InjectionProperty, InjectionConstructor, and InjectionMethod types, to show which class members should be used to make an injection. When setting InjectionContsructor and InjectionMethod for each parameter, a specific value can be indicated, or the required parameter can be resolved by a Unity container.

This method works best when injecting dependency to components with closed source code.

C#
public static class Resolver
   {
       private static UnityContainer _container = null;

       static Resolver()
       {
           _container = new UnityContainer();
           _container.RegisterType(typeof(IWorker), typeof(Worker), new HierarchicalLifetimeManager(), new InjectionMethod("Init", new ResolvedParameter(typeof(ILogger))));
       }
       public IUnityContainer Container { get { return _container; } }       
   }

You can also use the BuildUp method to inject dependencies in an existing object instance by passing it through container.

4. Setting configuration in XML

This method is used if configuration needs to be changed without recompiling the application. XML configuration is less flexible than the one set in the code, because it does not allow to describe custom types.

XML
<?xml version="1.0" encoding="utf-8" ?>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
 <container>
   <register type="UnityExample.IWorker, UnityExample"
             mapTo="UnityExample.Worker, UnityExample" />
 </container>
</unity>

Managing object life cycle

The main goal of a container is to manage lifetime of a requested object. Microsoft Unity framework has the following built-in lifetime managers:

  • TransientLifetimeManager โ€“ does not save an object in a container, instead creating an object with each request. This manager is used with RegisterType call by default.
  • ContainerControlledLifetimeManager โ€“ saves an object in a local variable. This allows object to live as long as a container.
  • ExternallyControlledLifetimeManager โ€“ saves WeakReference of an object. When using this manager and calling RegisterInstance, callerโ€™s code should control lifetime of an object, stored in a container. If RegisterType is used, this manager will return already existed instance of an object if it has one.
  • PerThreadLifetimeManager โ€“ saves objects in the ThreadStatic dictionary. This way every thread of an application will use its own set of objects. This manager is most often used in web projects when it is necessary to limit the lifetime of an object to the current request.
  • HierarchicalLifetimeManager โ€“ child containers should resolve their own instances without taking the parent one. This can help in Web app development in case of creating a child container for every request.
  • PerResolveLifetimeManager โ€“ allows to reuse a single instance within the same Resolve method call.

Disposable objects

When using containers, you can often need to manage lifetime of an object, which uses unmanaged resources: connection, database, web, or files. In this case, the most widely used solution is to implement the IDisposable interface in the consumer of this unmanaged resource and call the Dispose method when the work with this resource is finished. The main problem is the automatic call of this method in every dependency that was registered in a container and was used in a current cycle.

With respect to ASP.NET web applications, PerThreadLifetimeManager cannot be used because it basically never deletes objects from a container. In this case, we can apply an idea of a child container with the lifetime limited to the corresponding web request. At the end of the request, the Dispose method will be explicitly called (for example, in the Dispose method of controller). At the same time, the Dispose method is not called in the main, usually static container instance. Then when applying HierarchicalLifetimeManager, the Dispose method will be called for all hierarchy of registered objects when calling the Dispose method of a child container.

For Example:

C#
public static class Resolver
   {
       private static UnityContainer _container = null;

       static Resolver()
       {
           _container = new UnityContainer();
           _container.RegisterType(typeof(IWorker), typeof(Worker), new HierarchicalLifetimeManager());
       }
       public IUnityContainer Container { get { return _container; } }
       public static IUnityContainer CreateChild()
       {
           return _container.CreateChildContainer();
       }
       
   }

   public class HomeController : Controller
   {
       private readonly ContainerWrapper _child =  new ContainerWrapper(UnityTest.Container.Resolver.CreateChild());

       public ActionResult Index()
       {
           var fakeWork = _child.Resolve<IWorker>().DoWork();
           return new ContentResult() { Content = _child.Resolve<IWorker>().DoWork().ToString() };
       }

       protected override void Dispose(bool disposing)
       {
           if(disposing)
           {
               _child.Dispose();
           }
           base.Dispose(disposing);
       }
   }

In this case, a separate instance of controller with child container will be created for every separate request. Only one instance of an object will be created when calling the Index method. The Dispose method of this object (if it has one) will be called, when Dispose of the container is called.

The main disadvantage of this approach is that container becomes ServiceLocator (which in some cases can be considered anti-pattern), and using Unity Dependency Injection is impossible because we need the instance of container itself in order to free it.

Read also:
How to Handle Legacy Code: A Detailed Guide Based on a Real-Life Example

Aspect-oriented programming

Unity IoC framework, same as many other IoC frameworks, has built-in Aspect-oriented programming support (AOP). AOP is used to resolve cross-cutting dependencies.

There are two approaches to this solution โ€“ compile-time and runtime. In case of compile-time, code of the target class changes during compilation. Classes, which calls should be intercepted, are changed during compilation, and necessary interceptors are being added. This is called weaving. It gives better flexibility (interception of almost any call, including controllers and private methods) and have almost no impact on performance.

Unity uses the second type. Proxy classes are created in runtime, during execution, which imposes certain restrictions.

Interceptor types:

  • Transparent Proxy Interceptor
  • Interface Interceptor
  • Virtual Method Interceptor

Transparent Proxy Interceptor and Interface Interceptor are object-level interceptors. When client code creates an instance of an object, which calls should be intercepted (resolving dependencies via IoC or simply creating them via new), a handler connected to the object is created. Client code is using returned proxy as if it is an object of the necessary class. Method calls of this object get to the interceptor, where actions, described in the preprocessing method, are performed, and then are going through interceptor resulting in calls of the methods from a โ€œwrappedโ€ object. The returned value comes to the interceptor again, where postprocessing actions are executed, and then it is returned to the client code.

Instance interception is a most widespread way to configure AOP proxy.

  • Transparent Proxy Interceptor is used when an intercepted object is used in marshalling (implements MarshalByRefObject).
  • Interface interceptor is used when an intercepting object implements an interface describing all methods, which calls need to be intercepted.

Virtual Method Interceptor is a class-level interceptor. It dynamically creates an inherited class with implemented interceptor behavior. When client code receives dependency via IoC, container returns an instance of inherited class. Client code simply calls methods of this instance, while the code of the interceptor class is executed in the same way as during instance interception. But this approach has a number of restrictions. Only virtual methods can be intercepted. Objects should be created only with IoC containers. The main benefit of this approach is the best performance.

Example of implementation

We need to add transactionality on the level of business logic method call marked by the [TransactionMethod] attribute and automatically rollback a transaction if an unhandled exception occurs.

C#
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
   public class TransactionMethodAttribute : HandlerAttribute
   {
       public override ICallHandler CreateHandler(IUnityContainer container)
       {
           return new TransactionMethodCallHandler();
       }
   }

   public class TransactionMethodCallHandler : ICallHandler
   {
       public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
       {
           IMethodReturn result;
           using (TransactionScope transaction = new TransactionScope())
           {
               result = getNext()(input, getNext);

               if (result.Exception == null)
               {
                   transaction.Complete();
               }
           }
           return result;
       }

       public int Order { get; set; }
   }

           var _container = new UnityContainer();
           
           _container.RegisterType(typeof(IWorker), typeof(TransactionWorker), new HierarchicalLifetimeManager());
           _container.AddNewExtension<Interception>();

AOP performance

To evaluate and compare various types of interceptors, letโ€™s take look at the results shown by a proxy that logs exceptions occurred during method execution.

For 1 000 000 iterations:

TypeResult (sec)
Transparent Proxy Interceptor36.95
Interface Interceptor27.73
Virtual Method Interceptor3.15

As we can see Virtual Method Interceptor shows much better results (basically, marginally different from results without AOP), because it uses inherited class and the table of virtual functions instead of object-wrappers.

Read also:
Data Caching in ASP.NET

Conclusion

Using IoC and AOP allows to solve many problems when supporting low quality code. Introduction of these tools can be gradual, affecting only selected parts of application. By applying these approaches, we can gradually bring down the dependencies between components and thus save time for making changes in the solution.

We hope that this article served as a good beginner guide / brief Unity dependency injection tutorial and that provided information will help you to introduce changes to the code faster.

Usefull links

Article on Unity on MSDN

https://msdn.microsoft.com/en-us/library/ff647202.aspx

Dependency Injection with Unity (Microsoft patterns & practices) by Grigori Melnik, Fernando Simonazzi, Mani Subramanian and Dominic Betts

Performance test of various IoC containers

http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison

Have a question?

Ask our expert!

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.