Still tinkering around with Inversion of Control ( IoC ) and Castle's Windsor/Microkernel and how it could be used in various application architectures. See my previous post, Inversion of Control - Dependency Inversion Principle - Castle's Windsor/MicroKernel , for background.
In this example, I separate a "blog engine" into various projects:

If you expand the projects, you can get a better gist of the classes:

In this example, I have added a couple of infrastructure pieces which mimic the Enterprise Library 2.0 Data Access Application Block and Cache Manager API's:
- SqlDatabase - a Database Helper that implements IDatabase and used by BlogDAO
- WebCache - a Cache Manager that implements ICache, uses System.Web.Caching for in-memory caching, and also used by BlogDAO
Here is the rough database implementation-
public interface IDatabase
{
IDataReader ExecuteReader(DbCommand command);
DbCommand GetStoredProcCommand(string storedProcedureName);
void AddInParameter(DbCommand command, string name, DbType dbType, Object value);
}
public class SqlDatabase : IDatabase
{
private const string parameterPrefix = "@";
private string connectionString;
public SqlDatabase(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentNullException("connectionString");
this.connectionString = connectionString;
}
public DbConnection CreateConnection()
{
return new SqlConnection(connectionString);
}
public IDataReader ExecuteReader(DbCommand command)
{
if (command == null)
throw new ArgumentNullException("command");
DbConnection connection = CreateConnection();
command.Connection = connection;
connection.Open();
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
public DbCommand GetStoredProcCommand(string storedProcedureName)
{
if (string.IsNullOrEmpty(storedProcedureName))
throw new ArgumentNullException("storedProcedureName");
SqlCommand command = new SqlCommand(storedProcedureName);
command.CommandType = CommandType.StoredProcedure;
return command;
}
public void AddInParameter(DbCommand command, string name, DbType dbType, object value)
{
DbParameter parameter = new SqlParameter();
parameter.ParameterName = parameterPrefix + name;
parameter.DbType = dbType;
parameter.Value = value;
parameter.Direction = ParameterDirection.Input;
command.Parameters.Add(parameter);
}
}
and here is the rough caching implementation
public interface ICache
{
void Add(string key, Object value);
Object GetData(string key);
}
public class WebCache : ICache
{
private Cache cache = HttpRuntime.Cache;
public WebCache()
{
}
public void Add(string key, object value)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
if (value == null)
throw new ArgumentNullException("value");
cache.Insert(key, value);
}
public object GetData(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
return cache[key];
}
}
BlogDAO will use both the IDatabase and ICache services in its implementation:
public interface IBlogDAO
{
Blog GetBlogByBlogId(int blogId);
}
public class BlogDAO : IBlogDAO
{
private const string cacheKey = "Blog::{0}";
private IDatabase database;
private ICache cache;
public BlogDAO()
{
database = ServiceFactory.Instance.Create<IDatabase>();
cache = ServiceFactory.Instance.Create<ICache>();
}
public BlogDAO(IDatabase database, ICache cache)
{
if (database == null)
throw new ArgumentNullException("database");
if (cache == null)
throw new ArgumentNullException("cache");
this.database = database;
this.cache = cache;
}
public Blog GetBlogByBlogId(int blogId)
{
Blog blog = (Blog)cache.GetData(string.Format(cacheKey, blogId.ToString()));
if (blog == null)
{
DbCommand command = database.GetStoredProcCommand("GetBlogByBlogId");
database.AddInParameter(command, "BlogId", DbType.Int32, blogId);
using (IDataReader dr = database.ExecuteReader(command))
{
if (dr.Read())
{
BlogFactory factory = new BlogFactory();
blog = factory.Construct(dr);
cache.Add(string.Format(cacheKey, blogId.ToString()), blog);
}
}
}
return blog;
}
}
public class BlogFactory
{
public Blog Construct(IDataReader dr)
{
Blog blog = new Blog();
blog.Id = dr.GetInt32(0);
blog.Title = dr.GetString(1);
return blog;
}
}
Castle's Windsor/Microkernel XML File looks as follows. Ayende mentions Binsor as an alternative to XML.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<properties>
<connectionString>Data Source=.;Initial Catalog=Blog;Integrated Security=True</connectionString>
</properties>
<components>
<component id="SqlDatabase" service="BlogEngine.IDatabase, BlogEngine.Interfaces"
type="BlogEngine.Database.SqlDatabase, BlogEngine.Database">
<parameters>
<connectionString>#{connectionString}</connectionString>
</parameters>
</component>
<component id="WebCache" service="BlogEngine.ICache, BlogEngine.Interfaces"
type="BlogEngine.Caching.WebCache, BlogEngine.Caching" />
<component id="BlogDAO" service="BlogEngine.IBlogDAO, BlogEngine.Interfaces"
type="BlogEngine.Data.BlogDAO, BlogEngine.Data" lifestyle="transient">
</component>
</components>
</configuration>
I don't know how to get rid of my ServiceFactory Class ( Any Ideas? ) that uses Windsor behind the scenes and is the central service locator used in the application-
public sealed class ServiceFactory
{
private static ServiceFactory _instance = new ServiceFactory();
IWindsorContainer _container = new WindsorContainer(new XmlInterpreter("Components.xml"));
public static ServiceFactory Instance
{
get { return _instance; }
}
private ServiceFactory()
{
}
public T Create<T>()
{
return (T)_container[typeof(T)];
}
public T Create<T>(string name)
{
return (T)_container[name];
}
}
Now we can play with the pieces a bit. Here we inject the blog we want into the cache before we request it from BlogDAO. This should keep it from hitting the database, which it does-
Blog blog = new Blog();
blog.Id = 1;
blog.Title = "Test";
// Dump in cache using expected cache key
ICache cache = ServiceFactory.Instance.Create<ICache>();
cache.Add("Blog::1", blog);
// Get BlogDAO, which checks cache before calling database service.
IBlogDAO dao = ServiceFactory.Instance.Create<IBlogDAO>();
Blog theBlog = dao.GetBlogByBlogId(1);
Again, Windsor/Microkernel takes care of all the auto-wiring as mentioned in the previous article - Inversion of Control - Dependency Inversion Principle - Castle's Windsor/MicroKernel.
Hopefully this helps one thinking about how to use IoC in their applications.
by David Hayden
Posted
10-18-2006 3:27 PM
by
David Hayden