Introduction
In a previous CodeProject article, NHibernate Best Practices with ASP .NET, Generics and Unit Tests by Bill McCafferty, Core
project contains DAO
interfaces and domain objects while Data
project contains implementation of DAO
interfaces.
For performance reasons, since we do not want to iterate over collections to find a matching object, we use DAO
and thus NHibernate
to query for objects we are interested in. Data
project contains a reference to Core
project. We cannot add Data
project as reference to Core
project, as this will cause a circular reference and .NET does not allow us to use circular references. As a side effect or result of this restriction:
- We explicitly have to pass
DAO
instances to our domain objects during construction, or
- Set
DAO
property of our domain object with an instance of DAO
object which has to be created explicitly.
These two methods cause presentation layer developers (WinForm or Web) to write some repetitive code like this:
public void DoSomething()
{
IDaoFactory daoFactory = new DaoFactory();
ISomeDomainObjectDao dao = daoFactory.GetSomeDomainObjectDao();
SomeDomainObject domObj = new SomeDomainObject(dao);
SomeDomainObject domObj = new SomeDomainObject();
domObj.SomeDomainObjectDao = dao;
}
We can do a favour to presentation layer developers by automatically initializing internal DAO
objects. Below is a description of how we can achieve this.
Castle MikroKernel/Windsor
Castle
MikroKernel is an inversion of control container that provides us with a very rich set of extensibility. You can find out more information about Castle
/MikroKernel here.
In order to work with Castle
MikroKernel:
-
We need three assemblies from Castle
Project:
-
Castle.Model
-
Castle.Windsor
-
Castle.MikroKernel
-
We have to specify IoC components in our App/Web.config like the example provided below:
<configSections>
<section name="nhibernate" type="System.Configuration.NameValueSectionHandler,
System, Version=1.0.1.0,Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,
log4net, Version=1.2.9.0, Culture=neutral,
PublicKeyToken=b32731d11ce58905" />
<section name="castle"
type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler,
Castle.Windsor" />
</configSections>
<castle>
<components>
<component
id="SampleDaoFactory"
service="Fully qualified namespace where IDaoFactory interface resides"
type="Fully qualified namespace where IDaoFactory implementation resides">
</component>
<component
id="AnotherSampleDaoFactory"
service="Fully qualified namespace where IDaoFactory interface resides"
type="Fully qualified namespace where IDaoFactory implementation resides">
</component>
</components>
</castle>
DaoFactoryExposer Class Hierarchy
As you can see in the simplified model below, we work with three classes and one interface in this sample implementation.
DomainObject
: This is our domain object that needs a Dao
instance to query the database
IDaoFactory
: Specifies which DAO
objects are supported by the factory
DaoFactory
: Implements IDaoFactory
interface. Creates Dao
instances on request
DaoFactoryExposer
: Exposes a DaoFactory
instance by using Castle/MikroKernel IoC. As soon as we get a DaoFactory
instance, we will also be able to get any needed Dao
instance as specified by IDaoFactory
interface:
DaoFactoryExposer
using System;
using System.Collections.Generic;
using System.Text;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
namespace Bilgi.SampleProject.Core
{
public class DaoFactoryExposer
{
private IDaoFactory _daoFactory = null;
public DaoFactoryExposer( )
{
}
public IDaoFactory DaoFactory
{
get
{
if (_daoFactory != null)
{
return _daoFactory;
}
else
{
IWindsorContainer container = new WindsorContainer(new XmlInterpreter());
_daoFactory = container["SampleDaoFactory"] as IDaoFactory;
if (_daoFactory == null)
{
throw new TypeLoadException("Can not load dao factory from container!");
}
return _daoFactory;
}
}
}
}
}
IDaoFactory Interface
using System;
using System.Collections.Generic;
using System.Text;
namespace Bilgi.SampleProject.Core
{
public interface IDaoFactory
{
ISomeNeededDao GetSomeNeededDao( );
}
}
IDaoFactory Implementation (DaoFactory)
using System;
using System.Collections.Generic;
using System.Text;
namespace Bilgi.SampleProject.Data
{
public class DaoFactory:IDaoFactory
{
ISomeNeededDao _someNeededDao = null;
#region IDaoFactory Members
public ISomeNeededDao GetSomeNeededDao( )
{
if (_someNeededDao == null)
{
_someNeededDao = new SomeNeededDao();
}
return _someNeededDao;
}
#endregion
}
}
DomainObject
using System;
using System.Collections.Generic;
using System.Text;
namespace Bilgi.SampleProject.Core
{
public class DomainObject
{
#region Dao related
private DaoFactoryExposer _daoFactoryExposer = new DaoFactoryExposer();
IDaoFactory _daoFactory = null;
public IDaoFactory DaoFactory
{
get
{
if (_daoFactory == null)
{
return _daoFactoryExposer.DaoFactory;
}
else
{
return _daoFactory;
}
}
set
{
_daoFactory = value;
}
}
ISomeNeededDao _someNeededDao = null;
public ISomeNeededDao SomeNeededDao
{
get
{
if (_someNeededDao == null)
{
return DaoFactory.GetSomeNeededDao();
}
else
{
return _someNeededDao;
}
}
set { _someNeededDao = value; }
}
#endregion
public void AddOrder(Customer customer, Product product)
{
decimal customerBalance = SomeNeededDao.QueryCustomerBalance(customer);
if(customerBalance <= 0 )
{
throw new Exception("Not enough balance!");
}
}
}
}
History
- 2nd February, 2007: Initial post