In my previous
article
I try to elaborate interface dependency injection. Here I implemented a
simple example. For the injection we use the Microsoft Unity 2.0
container. For more information about the Unity 2.0 container please
refer
MSDN.
This sample has on Project entity class as mention below:
// Project entity class
public class Project {
public Project(string title, string id) {
this.title = title;
this.id = id;
}
private string title;
public string Title {
get {
return title;
}
set {
title = value;
}
}
private string id;
public string ID {
get {
return id;
}
set {
id = value;
}
}
}
To access Project information from database, we have one data layer class
and another is business layer class to implement business logic.
I am assuming that different user have different database so data layer classes
implementation vary for each database type.
So sample project have two data layer class one for sql server and another for Oracle.
// Project Data Layer to interact with sql server database.
public class ProjectDLForSQLServer : IProjectDL {
public Project GetProjectById(int id) {
Console.WriteLine("IProjectDL is resolved as ProjectDLForSQLServer.");
Project project = null;
// logic to access database and return the mapped data object to project entity.
return project;
}
public List<Project> GetProjects() {
List<Project> projects = null;
// logic to access database and return the mapped data object to project entity.
return projects;
}
}
Another datalayer class for Oracle.
// Project Data Layer to interact with sql server database.
public class ProjectDLForOracle : IProjectDL {
public Project GetProjectById(int id) {
Console.WriteLine("IProjectDL is resolved as ProjectDLForOracle.");
Project project = null;
// logic to get project from oracle database.
return project;
}
public List<Project> GetProjects() {
List<Project> projects = null;
// logic to get projects from oracle database.
return projects;
}
}
We
have single business layer class for project entity. If this business
class directly instantiate data layer class as mention below:
// Project Business Layer class
public class ProjectBL {
IProjectDL _projectDL;
public ProjectBL() {
// Here it directly instantiating data layer class.
_projectDL = new ProjectDLForSQLServer();
}
public ProjectBL(IProjectDL projectDL) {
_projectDL = projectDL;
}
public Project GetProjectById(int id) {
return _projectDL.GetProjectById(id);
}
public List<Project> GetProjects() {
return _projectDL.GetProjects();
}
}
So if we want to deploy same dll on another client using Oracle as a back end.
We need to change in business layer and recompile whole project once again.
And also need to maintain two version of same dll just because of this kind of tight coupling.
Interface
interception will solve this problem. By mean of interface interception
we can decide at runtime that which class implementation should our
code use.
By using Unity 2.0, we can achieve the same. Binding of
interface and class implementation can be done either in code or by
using xml configuration file.
Here I am using second approach i.e. by xml configuration.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<!--A reference to unity dll-->
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<!--Defing assembly reference and namespace of policy injection and our custom dll-->
<assembly name="Microsoft.Practices.EnterpriseLibrary.PolicyInjection"/>
<namespace name="Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers"/>
<assembly name="InterfaceInterception" />
<namespace name="InterfaceInterception, InterfaceInterception"/>
<assembly name="ITDataService" />
<namespace name="ITDataService, ITDataService"/>
<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>
<alias alias="IProjectDL" type="InterfaceInterception.IProjectDL, InterfaceInterception"/>
<alias alias="ProjectDLForSQLServer" type="InterfaceInterception.ProjectDLForSQLServer, InterfaceInterception"/>
<alias alias="ProjectDLForOracle" type="InterfaceInterception.ProjectDLForOracle, InterfaceInterception"/>
<alias alias="IewAppsSessionHandler" type="ITDataService.IewAppsSessionHandler, ITDataService"/>
<alias alias="ewAppWebSessionHandler" type="ewAppWeb.ewAppWebSessionHandler, ewAppWeb"/>
<container>
<extension type="Interception"/>
<!-- Here is the mapping of interface to class -->
<register name="IProjectMapper" type="IProjectDL" mapTo="ProjectDLForOracle">
<interceptor type="TransparentProxyInterceptor"/>
<interceptionBehavior type="PolicyInjectionBehavior"/>
</register>
</container>
</unity>
</configuration>
Above config file have complete configuration. To use this unity container class
we have created a wrapper class "UContainer"
public class UContainer : IContainerAccessor {
private static IUnityContainer _container = null;
public static IUnityContainer Container {
get {
return _container;
}
set {
_container = value;
}
}
#region IContainerAccessor Members
IUnityContainer IContainerAccessor.Container {
get {
return Container;
}
}
#endregion
}
public interface IContainerAccessor {
IUnityContainer Container {
get;
}
}
Now all the required configuration and class implementation is completed.
Now time to use configuration to know the binding at runtime. Our business
layer class now have little different implementation.
// Project Business Layer class
public class ProjectBL {
IProjectDL _projectDL;
public ProjectBL() {
// Here we are calling unity container to resolve the IProjectDL implementation.
_projectDL = UContainer.Container.Resolve<IProjectDL>("IProjectMapper");
}
public ProjectBL(IProjectDL projectDL) {
_projectDL = projectDL;
}
public Project GetProjectById(int id) {
return _projectDL.GetProjectById(id);
}
public List<Project> GetProjects() {
return _projectDL.GetProjects();
}
}
In above class I don't use any class, to resolve the current implementation to be use, use unity container.
We just missing one thing that is initialization of UContainer class.
public class UContainer : IContainerAccessor {
private static IUnityContainer _container = null;
public static IUnityContainer Container {
get {
return _container;
}
set {
_container = value;
}
}
#region IContainerAccessor Members
IUnityContainer IContainerAccessor.Container {
get {
return Container;
}
}
#endregion
}
public interface IContainerAccessor {
IUnityContainer Container {
get;
}
}
I
am using a console application to test our sample code. So in Program
class, main function I am initializing UContainer class as mention
below:
class Program {
static void Main(string[] args) {
IUnityContainer container = new UnityContainer();
UnityConfigurationSection configSection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
configSection.Configure(container);
container.AddNewExtension<EnterpriseLibraryCoreExtension>();
UContainer.Container = container;
ProjectBL bl = new ProjectBL();
bl.GetProjectById(1);
Console.ReadLine();
}
}
Unity
container instance look into default config file of project for
configuration information. If any error is found in configuration file
like some syntax error or some of the defined types are not able to
resolve an runtime exception has been thrown.
So when you run application, you get the following output.
If we change the configuration mapping to ProjectDLForSqlServer output will be
I hope this article will help.