If you have a good understanding of IoC (Inversion of Control), DI(Dependency Injection) and Multi-layer architecture then continue reading this article otherwise have a look at this article – An idea of a three tier application using IoC (Inversion of Control), DI (Dependency Injection), and perfectly decoupled layers
Container
A ‘Container’ as the name implies – will contain some objects for further use in your project. These stored objects are mainly the Business Layer Objects (BLL) and the Data Access Layer Objects (DAL). We store them and then retrieve them as needed in the Application.
Sometimes a BLL object may depend on a DAL object. The Container will load (instantiate) them separately. Then the problem is: how will we inform the Container about their inter dependency. Here comes the role of Dependency Injection. The Container will use DI (Dependency Injection) to inject the dependent-object in to the depende-object mostly through the constructor. I will not go in detail of DI as there are lots of article about it the web.
I will stick with the Container. Container mainly implements IoC(Inversion of control): another software engineering terminology which I am not going to discus here but you can find a clear concept from my post here. In short with IoC we are actually controlling the loading of all the objects in the Container. Basically this is the concept behind all the Containers like Unity, Castle Windsor or StructureMap.
Dependency Injection and use of IoC container is getting popular day by day. But have we been ever curious about this – why are they useful? This is all about improving the performance and maintainability of code. Using IoC Container you can have these two benefits
- Your code is easy to maintain
- Your code is easy to maintain because all your BLL and DAL objects are loaded is a centralized place called Composition-Root. So if you want to replace a particular object – just do it in Composition-Root.
- Gives you extra performance
- In the memory allocation perspective – every new heap allocation takes a significant time and is subject to a task of garbage collector. Hence uncontrolled object loading and releasing affects the performance. But a IoC container will help us to minimize the scattered object instantiation and thus the performance will improve.
In this article I am going to implement a very basic Container in a three tier Web-application. The design is as bellow
Fig: A three tier application architecture
The detail of this design has been described in the post – An idea of a three tier application using IoC(Inversion of Control), DI(Dependency Injection) and perfectly decoupled layers.
I am not going to describe this design here, rather I am going to modify the design and introduce a Container in our Composition-Root (i.e.,
CRoot
) layer. We will call our container object – ControlsContainer
. Possibly it will be a single-tone. There will be another object Root that will use this ControlsContainer
object.
A three tier application with Container
This ControlsContainer
will maintain a dictionary of objects where all the BLL and DAL layer objects will be registered and indexed. These objects will be fetched as needed throughout the project.
Let's start with a very preliminary skeleton of our ControlsController
class.
public sealed class ControlsContainer
{
private static readonly ControlsContainer _cc = new ControlsContainer();
public static ControlsContainer CContainerInstance
{
get {
return _cc;
}
}
public delegate object CreateInstance(ControlsContainer container);
Dictionary Factories;
private ControlsContainer()
{
Factories = new Dictionary();
}
}
The code is self descriptive with the comments. Firstly we are creating the object as singleton – so that we don’t have to instantiate it over and over again. Then we put a delegate to hold the pointer of a function that will create a new object (i.e., BLL and DAL object). Then we create a dictionary ‘Factories’ that will store all the objects reference. In the constructor the dictionary has been instantiated. This dictionary will be indexed with the Type of the objects and these objects will be created with the help of the delegate ‘CreateInstance
‘. For more understanding on delegate you can read this article ‘Ways of Delegation in .NET (C# and VB)‘.
Now is the time to create a function RegisterInterface
in the
ControlsContainer
object that will register an object by adding the type of the object and the delegate that will create that object in the dictionary. But certainly it will not instantiate the object.
public void RegisterInterface(CreateInstance ci)
{
if (ci == null)
throw new ArgumentNullException("ci");
if (Factories.ContainsKey(typeof(T)))
throw new ArgumentException("Type already registered");
Factories.Add(typeof(T), ci);
}
We need another function Resolve
that will call the appropriate delegate function according to the Type, instantiate the object and return the instance.
public T Resolve()
{
if (!Factories.ContainsKey(typeof(T)))
throw new ArgumentException("Type not registered");
CreateInstance creator = (CreateInstance)Factories[typeof(T)];
return (T)creator(this);
}
public object Resolve(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!Factories.ContainsKey(type))
throw new ArgumentException("Type not registered");
CreateInstance creator = (CreateInstance)Factories[type];
return creator(this);
}
We have some utility function that will check the dictionary for a Type.
public bool IsInterfaceRegistered()
{
return Factories.ContainsKey(typeof(T));
}
public bool IsInterfaceRegistered(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return Factories.ContainsKey(type);
}
Finally our ControlsContainer
class will look like this
public sealed class ControlsContainer
{
private static readonly ControlsContainer _cc = new ControlsContainer();
public static ControlsContainer CContainerInstance
{
get {
return _cc;
}
}
public delegate object CreateInstance(ControlsContainer container);
Dictionary Factories;
private ControlsContainer()
{
Factories = new Dictionary();
}
public void RegisterInterface(CreateInstance ci)
{
if (ci == null)
throw new ArgumentNullException("ci");
if (Factories.ContainsKey(typeof(T)))
throw new ArgumentException("Type already registered");
Factories.Add(typeof(T), ci);
}
public T Resolve()
{
if (!Factories.ContainsKey(typeof(T)))
throw new ArgumentException("Type not registered");
CreateInstance creator = (CreateInstance)Factories[typeof(T)];
return (T)creator(this);
}
public object Resolve(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (!Factories.ContainsKey(type))
throw new ArgumentException("Type not registered");
CreateInstance creator = (CreateInstance)Factories[type];
return creator(this);
}
public bool IsInterfaceRegistered()
{
return Factories.ContainsKey(typeof(T));
}
public bool IsInterfaceRegistered(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
return Factories.ContainsKey(type);
}
}
Root
Now let's look at the Root
class that will be using the ControlsContainer
object. The Root object will basically instantiate the singleton of the
ControlsContainer
and then will register all the BLL and DAL objects in the dictionary.
public class Root
{
public ControlsContainer MyContainer { get; set; }
public Root()
{
ControlsContainer _container = ControlsContainer.CContainerInstance;
this.MyContainer = _container;
_container.RegisterInterface( ( ControlsContainer _c ) => new SqlProductRepository());
_container.RegisterInterface((ControlsContainer _c ) => new ProductService(_c.Resolve()));
}
}
Notice that the BLL and DAL objects are registered with the Interface Types – not the original object Types. This actually decouples the view layer from the BLL and DAL totally. If any object needs a change in the BLL and DAL layer then it doesn’t reflect any change in the View layer as long as the BLL and DAL implements the interfaces.
Also notice at the last line of the class where we are registering the IProductService
in the container – we are also passing the dependency of
ISqlProductRepository
in the constructor of the ProductService
class. Thus we are using dependency injection to decouple the BLL and DAL layer.
View Layer
Now it is time to look at our view. we are implementing a MVC web-application in .NET as our view. In our view layer we will just have to use the Root object and the Interfaces.
At the starting point of our web application (Application_Start
event at
global.asax.ce file) we will have to create our custom controller factory and pass the root object in it. If you are not familiar with custom controller factory hit here – its very simple. Our custom controller factory - MyControllarFactory
object will get the container –
ControlsContainer
object from the property of the Root
object. Then at every request invoke the our controller factory - MyControllarFactory
object will do the following jobs:
- Create appropriate controller instance.
- Query the container to get the BLL object (i.e.,
ProductSerice
) for that controller.
- Pass this BLL object to the constructor of the controller as dependency injection.
Note that this BLL object will be referenced by an interface (i.e., IProdctService
).
Below is the MyControllarFactory
class:
public class MyControllarFactory : DefaultControllerFactory
{
private ControlsContainer _container { get; set; }
private MyControllarFactory()
{
}
public MyControllarFactory(Root _root)
{
if (_root == null)
throw new ArgumentNullException("root");
_container = _root.MyContainer;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return null;
if (!typeof(IController).IsAssignableFrom(controllerType))
throw new System.ArgumentException("Type does not subclass IController", "controllerType");
object[] parameters = null;
ConstructorInfo constructor = controllerType.GetConstructors().FirstOrDefault(c => c.GetParameters().Length > 0);
if (constructor != null)
{
ParameterInfo[] parametersInfo = constructor.GetParameters();
parameters = new object[parametersInfo.Length];
for (int i = 0; i < parametersInfo.Length; i++)
{
ParameterInfo p = parametersInfo[i];
if (!_container.IsInterfaceRegistered(p.ParameterType))
throw new ApplicationException("Can't instanciate controller '" +
controllerType.Name + "', one of its parameters is unknown to the IoC Container");
parameters[i] = _container.Resolve(p.ParameterType);
}
}
try
{
return (IController)Activator.CreateInstance(controllerType, parameters);
}
catch (Exception ex)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
"error creating controller", controllerType), ex);
}
}
}
Then the controller will be able to use that service instance to query the data from DB.
public class HomeController : Controller
{
IProductService _ps;
public HomeController(IProductService prSrv)
{
_ps = prSrv;
}
public ActionResult Index()
{
ViewBag.Message = "Total Product: " + _ps.GetSqlProductList().Count().ToString();
return View();
}
public ActionResult About()
{
return View();
}
}
Notice the HomeController
uses the interface to call the service layer (BLL) functionality.
Up to now we have successfully implemented a container – ControlsContainer
that will register all our back-end objects and will draw them as par requirements from the view layer.
Here I have used a very simple container to clear up the understanding of how container helps us in our application, so that in practical projects we can use some more feature rich containers like Unity, Castle Windsor or StructureMap.
Please comment " src="http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif" />
Reference