David Hayden [MVP C#]

Sponsors

The Lounge

News

  • CodeBetter.Com Home

Other Links

Teas

Patterns & Practices

Florida .NET Developer

Book Reviews

Tampa ASP.NET MVC Developer Group

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Another Inversion of Control Example With Infrastructure and DAO Components

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

[Advertisement]

Comments

Tomas Restrepo wrote re: Another Inversion of Control Example With Infrastructure and DAO Components
on 10-18-2006 8:05 PM

David: Regarding the ServiceFactory thingie, I favore the approach shown by ayende of using a static IoC class that holds the container. Besides being much less to type everytime, it's more readable, and just as easy to use for unit testing.

http://www.ayende.com/Blog/IoCAndFluentInterfaces.aspx

David Hayden wrote re: Another Inversion of Control Example With Infrastructure and DAO Components
on 10-18-2006 8:45 PM

Good point, Tomas. Since I have sealed the singleton, I might as well make it a static class since one cannot inherit from it anyway. That would reduce the typing.

Although my main reason for sealing it is because it feels like an un-necessary class and I was hoping to dump it. I am not sure why the Windsor Container makes us wrap it up in another class. IMHO, this just creates extra work with no noticeable benefits.

Thanks for the link. Even Ayende has a similar wrapper, which means they might as well include a wrapper in the package. I think :)

Christopher Steen wrote Link Listing - October 18, 2006
on 10-18-2006 9:45 PM

Remote Desktop On A Non-Standard Port [Via: Haacked ] Why Oh Why Couldn't WebPermission Be Part Of Medium...

ScottBellware wrote re: Another Inversion of Control Example With Infrastructure and DAO Components
on 10-19-2006 4:58 PM

Why was it necessary to put the interfaces into a separate component?  This would more or less de-contextualize the interfaces from the classes in the component that needs it, and ultimately tend to obscure the intention and locality of the concern represented by the interface.

I would have expected the IBlogDAO client interface to live with the component that houses the classes that it serves.  In the case of your project, I would have expected it to be in BlogEngine.Core since it is the data access client interface for Blog.

David Hayden wrote re: Another Inversion of Control Example With Infrastructure and DAO Components
on 10-19-2006 7:56 PM

It wasn't necessary. The interfaces could have been in BlogEngine.Core.