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.
Senior Software Developer of Web Development Team
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.
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.
2. Setting dependencies via attributes
DependencyAttribute, a constructor has
InjectionConstructorAttribute, and a method has
DependencyAttribute can also be set for constructor parameters and injection methods.
DependencyAttribute to a property or a parameter, you can indicate the name of the dependency.
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
InjectionMethod types, to show which class members should be used to make an injection. When setting
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.
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 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
RegisterTypecall by default.
ContainerControlledLifetimeManager– saves an object in a local variable. This allows object to live as long as a container.
WeakReferenceof an object. When using this manager and calling
RegisterInstance, caller’s code should control lifetime of an object, stored in a container. If
RegisterTypeis used, this manager will return already existed instance of an object if it has one.
PerThreadLifetimeManager– saves objects in the
ThreadStaticdictionary. 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.
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
Dispose method will be called for all hierarchy of registered objects when calling the
Dispose method of a child container.
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.
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.
- 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
- 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.
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:
Transparent Proxy Interceptor
Virtual Method Interceptor
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.
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.
Article on Unity on MSDN
Dependency Injection with Unity (Microsoft patterns & practices) by Grigori Melnik, Fernando Simonazzi, Mani Subramanian and Dominic Betts
Performance test of various IoC containers