Hello World,
I've been dangling with IoC and DI for quite sometime and thought it would be nice to sum up my gotchas in a blog post. I'll try to layout this post so that it starts with the assumption that the reader is an absolute newbie to Microsoft's Enterprise Library but has some understanding of what IoC (Inversion of Control) is and what DI (Dependency Injection) containers try to achieve. For an understanding about those concepts, I highly recommend
Martin Fowler's post as a starting point.
I'm writing this from a machine where I've got VS 2012 but this pretty much works on VS 2010 as well.
I. File -> New Project
Fire up Visual Studio and create a new web project. For starters, I've used the ASP.NET WebForms application template:
II. Add some code!
For the purpose of this demo, I'll build an app that fetches users from a database table and dumps them on screen. Keeping it simple will let us focus on the key learning of how Unity is setup and used in a web application.
Let's add a class library now:
Right click the solution and choose Add -> New Project and choose the type as Class Library. Let's call it as Data. We're going to define a generic repository interface and a DB context to connect to the database here. I'm not going to implement the complete thing but only the necessary parts that would convey the concepts. Take a look at this
wonderful post by
Jonas Gauffin to understand why a repository pattern fits the bill almost in all business applications.
We now add a repository interface as below:
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity Get(TKey id);
TEntity GetAll();
}
Now we derive a IUserRepository from this:
public interface IUserRepository : IRepository<User, string>
{
/// <summary> Finds all Users. </summary>
/// <returns></returns>
IEnumerable<User> FindAll();
/// <summary> Finds User by id. </summary>
/// <param name="id">The id.</param>
/// <returns></returns>
IEnumerable<User> FindById(string id);
}
Next we create a IDbContext to talk to the database
public interface IDbContext : IDisposable
{
/// <summary>
/// This method returns new instance of command with same instance of connection on source type
/// This might create problem in parallel execution of multiple data readers with same instance of db connection
/// </summary>
/// <param name="connectionString">The connection string.</param>
/// <param name="commandText">The command text.</param>
/// <param name="args">The args.</param>
/// <returns></returns>
IDbCommand CreateCommand(string connectionString, string commandText = "", params IDbDataParameter[] args);
/// <summary>
/// This method returns new instance of command with same instance of connection on source type
/// This might create problem in parallel execution of multiple data readers with same instance of db connection
/// </summary>
/// <param name="connectionString">The connection string.</param>
/// <param name="commandTimeOut">The command time out.</param>
/// <param name="commandText">The command text.</param>
/// <param name="args">The args.</param>
/// <returns></returns>
IDbCommand CreateCommand(string connectionString, int commandTimeOut, string commandText = "", params IDbDataParameter[] args);
/// <summary>
/// This method will return new instance of command with new instance of connection
/// It is useful in case of multiple threads executing data reader and db commands
/// </summary>
/// <param name="connectionString">The connection string.</param>
/// <param name="commandText">The command text.</param>
/// <param name="args">The args.</param>
/// <returns></returns>
IDbCommand CreateNewCommand(string connectionString, string commandText = "", params IDbDataParameter[] args);
/// <summary>
/// This method will return new instance of command with new instance of connection
/// It is useful in case of multiple threads executing data reader and db commands
/// </summary>
/// <param name="connectionString">The connection string.</param>
/// <param name="commandTimeOut">The command time out.</param>
/// <param name="commandText">The command text.</param>
/// <param name="args">The args.</param>
/// <returns></returns>
IDbCommand CreateNewCommand(string connectionString, int commandTimeOut, string commandText = "", params IDbDataParameter[] args);
}
Let us now create a DTO (data transfer object) that mirrors what we want to show up on the screen:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime DateOfBirth { get; set; }
}
This is what the
Users table definition looks like
USE [SudhanshuTest]
GO
/****** Object: Table [dbo].[Users] Script Date: 8/2/2013 5:11:22 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[Email] [nvarchar](100) NULL,
[DateOfBirth] [datetime] NULL,
PRIMARY KEY NONCLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
Here is a sample IDbContext class implementation:
public class DbContext : IDbContext
{
private readonly IDictionary<string, SqlConnection> _connections;
private readonly string _sqlConnection;
[InjectionConstructor]
public DbContext()
{
_sqlConnection = ConfigurationManager.ConnectionStrings["UserDb"].ConnectionString;
_connections = new Dictionary<string, SqlConnection>();
}
public IDbCommand CreateCommand(string connectionString, string commandText = "", params IDbDataParameter[] args)
{
return CreateDbCommand(CreateConnection(connectionString), commandText, args);
}
public IDbCommand CreateCommand(string connectionString, int commandTimeOut, string commandText = "", params IDbDataParameter[] args)
{
var command = CreateNewCommand(connectionString, commandText, args);
command.CommandTimeout = commandTimeOut;
return command;
}
public IDbCommand CreateNewCommand(string connectionString, string commandText = "", params IDbDataParameter[] args)
{
var connection = GetConnection(_sqlConnection);
return CreateDbCommand(connection, commandText, args);
}
public IDbCommand CreateNewCommand(string connectionString, int commandTimeOut, string commandText = "", params IDbDataParameter[] args)
{
var command = CreateCommand(connectionString, commandText, args);
command.CommandTimeout = commandTimeOut;
return command;
}
public void Dispose()
{
foreach (var sqlConnection in _connections)
{
//TODO: add open checks...
sqlConnection.Value.Dispose();
}
}
private IDbConnection CreateConnection(string connectionString)
{
var connection = new SqlConnection(connectionString);
connection.Open();
return connection;
}
private IDbCommand CreateDbCommand(IDbConnection connection, string commandText = "", params IDbDataParameter[] args)
{
var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = commandText;
foreach (var sqlParameter in args)
{
command.Parameters.Add(sqlParameter);
}
return command;
}
private IDbConnection GetConnection(string source)
{
SqlConnection connection;
if (!_connections.TryGetValue(source, out connection))
{
connection = new SqlConnection(_sqlConnection);
connection.Open();
_connections.Add(source, connection);
}
return connection;
}
}
Now add another class lib project called Data.SqlServer
Next, we create a base class for our repositories that would have the scaffolding required to get a DB connection
public abstract class RepositoryBase
{
internal IDbContext DbContext { get; private set; }
protected RepositoryBase(IDbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException("dbContext");
}
DbContext = dbContext;
}
public T Cast<T>(object columnValue)
{
if (columnValue is DBNull || columnValue == null)
{
return default(T);
}
return (T)Convert.ChangeType(columnValue, typeof(T));
}
}
We now create a UserRepository that derives from both the IUserRepository and the the RepositoryBase above. I'll just show the method that fetches users:
public IEnumerable<User> FindAll()
{
var result = new List<User>();
using (var command = DbContext.CreateCommand(_connectionStrings, "dbo.GetAllUsers"))
{
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
result.Add(new User
{
Id = Cast<int>(reader["Id"]),
FirstName = Cast<string>(reader["FirstName"]),
LastName = Cast<string>(reader["LastName"]),
DateOfBirth = Cast<DateTime>(reader["DateOfBirth"]),
Email = Cast<string>(reader["Email"])
}
);
}
}
}
return result;
}
III. Wire it all up with Unity.
The easiest way to add Unity and related stuff is through NuGet. Right click you project and choose Manage NuGet packages
Were going to need the reference to the following assemblies: (check
this link)
Microsoft.Practices.EnterpriseLibrary.Common.dll
Microsoft.Practices.ServiceLocation.dll
Microsoft.Practices.Unity.dll (current version 3.0.1304.0 at the time of this writing)
Microsoft.Practices.Unity.Interception.dll
The corresponding NuGet packages are:
Enterprise Library common
Enterprise library policy injection
Unity Interception extension
Unity
We are going to use the wonderful UnityContainerExtension class for our purpose to cleanly encapsulate the wiring up logic. In the Data.SqlServer class library project, which contains the concrete implementations of the interfaces that would like Unity to resolve, we would add a class called Module.cs with the following code:
public class Module : UnityContainerExtension
{
protected override void Initialize()
{
Container.RegisterType<IDbContext, DbContext>(new PerHttpRequestLifetime());
Container.RegisterType<IUserRepository, UserRepository>(new PerHttpRequestLifetime(), new InjectionConstructor(new ResolvedParameter<IDbContext>()));
}
}
Things to note:
We derive from UnityContainerExtension class from the Microsoft.Practices.Unity namespace
This class has a single method called Initialize which has the signature as shown above.
Notice the order of the RegisterType statements, we first specify a mapping for the IDbContext to its concrete type and then the IUserRepository.
The concrete UserRepository has a constructor that accepts a parameter of the type IDbContext. That's why we specify the "InjectionContructor" and its parameter like so:
new InjectionConstructor(new ResolvedParameter<IDbContext>())
Next we need to tell our web app to initialize and setup the container. The most used way here is the Application_start method in the Global.asax.cs file. This looks like the below;
void Application_Start(object sender, EventArgs e)
{
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterOpenAuth();
RouteConfig.RegisterRoutes(RouteTable.Routes);
SetupDependencyInjection();
// Code that runs on application startup
}
The SetupDependencyInjection method looks like this:
private void SetupDependencyInjection()
{
Container = new UnityContainer();
Container.AddExtension(new Data.SqlServer.Module());
DependencyResolver.SetResolver(Container);
}
DependencyResolver is a small static helper class that looks like below:
public class DependencyResolver
{
private static IUnityContainer _container;
public static void SetResolver(IUnityContainer resolver) //Change to interface...
{
_container = resolver;
}
public static T Get<T>()
{
return _container.Resolve<T>();
}
public static T Get<T>(string name)
{
return _container.Resolve<T>(name);
}
public static void BuildUp(object item)
{
_container.BuildUp(item.GetType(), item);
}
public static IUnityContainer Resolver { get { return _container; } }
}
Note that for MVC apps, you'd normally make this call:
System.Web.Mvc.DependencyResolver.SetResolver(Container);
Since we do not have an MVC scope here, the DependencyResolver comes into picture.
That's pretty much it. You should now be able to connect to the database and run this to see the users on the screen! Unity is nicely resolving your dependencies without tight coupling the interfaces and implementations.
Further reading on Unity with a superb developer's guide and reference implementation is
here.
Hope this presents a nice bootstarpper introduction that will whet the appetite of the interested :). I will try posting the code on soon.
EDIT: Some information on a reference implementation for Unity 5 and 6 (from the Readme)
-----------------------------------------------------------------------------------------------
aExpense Reference Implementation (RI)
Summary: This package contains the aExpense Reference Implementation to showcase the use of Enterprise Library application blocks. There are two versions of the reference implementation: one using Enterprise Library 5.0 and another one using Enterprise Library 6.0. The objective was to assist you not only with getting acquianted with Enterprise Library, but also with migrating from the previous version.
The most up-to-date version of the documentation and release notes is available online:
http://go.microsoft.com/fwlink/p/?LinkID=290917
Microsoft patterns & practices
http://msdn.microsoft.com/practices
-----------------------------------------------------------------------------------------------
Happy Coding