Dear Friends,
Dependency injection is the buzz word in asp.net world. There are lots of frameworks available today like Sprint.Net , Unity Framework , Pico , Avalon and lot others. A typical DI framework as certain components.
1. Container : This is a mapper object which helps in dependency resolution , A container is a compartment that houses some sort of abstraction within its walls. Typically, responsibility for object management is taken over by whatever container is being used to manage those objects.
2. Mapping Specifier : This is some kind of configurable resource which helps in dependency resolution.
3. Object Factory : This generally is a Factory method which uses the mapping specifier to fill the container and ultimately delivers the desired object to the client.
Benefits : DI frameworks offer a lot of benefits, some of them are listed below.
1. Less code : When you employ a DI framework, you save writing a lot of boilerplate code, its different components can be used to supply a lot of commonly required information.
2. Unit Testing : DI frameworks facilitate high degree of component decoupling, this helps in unit testing.
Developing a custom DI framework : It’s not difficult however to develop such a framework, and once done it really gives a nice design to our application. So in this article we’ll see how we can develop one such framework.
We’ll contemplate on the following aspects of our approach.
Abstraction : Abstraction is an important OOP’s concept and generally refers to the separation of common concerns from the overall logic.
Whether you work with an MVC application or an MVP,MVVP , MVVM application , you’ll always work with objects called as entities/models etc.. , they describe the actors within your applications.
So, our framework will abstractly define the notion of an entity.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Abstraction
{
public abstract class EntityBase
{
public abstract bool ValidateEntity();
}
}
This base class will serve the purpose of defining and entity in a generalized way.
Abstraction of Operations : This refers to the discrete operations in which an entity can participate. In typical service applications we can generalize them as CRUD operations.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Interfaces
{
public interface IEntityOperations
{
EntityBase Create(EntityBase e);
EntityBase Update(EntityBase e);
IEnumerable<EntityBase> GetAll();
EntityBase Delete(EntityBase e);
}
}
Putting the pieces together : Having defined the above basic skeleton , we are all set to put it to some real use. Let’s start by defining an entity (Person) for us to work.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Interfaces;
namespace Entities
{
public class Person : EntityBase
{
public string Name { get; set; }
public override bool ValidateEntity()
{
throw new NotImplementedException();
}
}
}
NOW, Let’s also define a business layer for our application.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FrameWork;
using Entities;
using Interfaces;
namespace BusinessLogic
{
[AssociatedEntity(typeof(Person))]
public class PersonBl : IEntityOperations
{
#region IEntityOperations Members
public EntityBase Create(EntityBase e)
{
return new Person { Name = “Jackie” };
}
public EntityBase Update(EntityBase e)
{
return new Person { Name = “Jackie” };
}
public IEnumerable<EntityBase> GetAll()
{
throw new NotImplementedException();
}
public EntityBase Delete(EntityBase e)
{
throw new NotImplementedException();
}
#endregion
}
}
The important point to note here is the attribute “AssociatedEntity“ , which helps in defining the mapping between an entity and its worker class. Here goes the implementation of this attribute.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Interfaces;
namespace FrameWork
{
[AttributeUsage(AttributeTargets.Class)]
public class AssociatedEntity: Attribute
{
Type _t;
public AssociatedEntity(Type t)
{
_t = t;
}
public Type AssociatedType
{
get
{
return _t;
}
}
}
}
The factory method Implementation : Now when we’ve the mapping defined , it’s the time to create a factory class which resolves our dependency at runtime to produce the desired object. This method takes the help of our attribute to establish the relationship between the entity/model and it’s corresponding BLL class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Interfaces;
using System.Reflection;
using System.IO;
using System.Configuration;
namespace FrameWork
{
public static class ServiceContext
{
public static IEntityOperations CreateService<T>(T desiredType) where T : EntityBase
{
Assembly asm = Assembly.LoadFile(ConfigurationManager.AppSettings["BLLAssembly"]);
foreach
(Type typ in asm.GetTypes())
{
foreach (AssociatedEntity attrib in typ.GetCustomAttributes(typeof(AssociatedEntity), false))
{
if (attrib.AssociatedType == typeof(T))
return Activator.CreateInstance(typ) as IEntityOperations;
}
}
return null;
}
}
}
In this method, We are essentially looking for the business logic DLL to look for the BLL classes and their associated entities. This method can also be used to fill a container like a Dictionary to store the mapping of entity and BLL class.
Now we are ready to use it for a client application. Here’s a small console application which makes use of our framework.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Entities;
using FrameWork;
using Interfaces;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Person p = new Person { Name = “bruce“ };
IEntityOperations per=ServiceContext.CreateService<Person>(p);
p.Name=”jackie”;
p = per.Update(p) as Person;
}
}
}
Explanation & Benefits :
- Our console application (client) doesn’t know anything about which object is responsible for working with Person entity, all it knows is IEntityOperations , and expects the CRUD operations to have defined, which is exactly why “IEntityOperations” has been created for.
- Since there is no link b/w the client and the server, both can developed simultaneouy.
3. Testing can be done even before the actual implementation is provided.