Pragmatic Developer

Ali Özgür

Bookmark Blog

Add to Technorati Favorites

Google Talk

Chat with Ali Özgür

Purchase PragmaSQL from

Calendar

«  December 2008  »
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234
View posts in large calendar

Tag Cloud

Don't show

    Authors

    Recent Comments

    Banners




    Introduction

    In previous article NHibernate Best Practices with ASP .NET, Generics and Unit Tests by Bill McCafferty NHSessionManager is configured via App/Web config simply to work with NHibernate core assembly. Since NHSessionManager and other assemblies (Core, Data and Test) are located in the same solution this simple way is enough for all practical purposes. But for sake of code resuse we can think to reorginize NHibernate utility classes (NHSessionManager, DomainObject, NHGenericDao) and interfaces (IGenericDao) in a seperate solution/project. When this is the case we need a more generic way to specify NHSessionManager configuration. Another article Using NHibernate with Multiple Databases again by Bill McCafferty is a very good example how to extend configuration system of .NET

    Extending Configuration System

    We need a configuration section handler and configuration element specification in order to specify multiple NHibernate asemblies via App/Web config.
    Our goal is to handle a configuration section like this one

     
    
    
     <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"/>
        <section name="nHibernateAssemblies" type=" NHibernateSample.Common.Configuration.NHAssembliesSection, NHibernateSample.Common"/>
      </configSections>
      
      <nHibernateAssemblies>
        <NhAssemblies>
          <clearAssemblies />
          <NhAssembly name="Core assembly 1" assembly="ProjectName1.Core"/>
          <NhAssembly name="Another core assembly" assembly="ProjectName2.Core"/>
        </NhAssemblies>
      </nHibernateAssemblies> 
    
    
    

    NHAssemblyElement

    NHAssemblyElement is inherited from ConfigurationElement placed in System.Configuration namespace
    NHAssemblyElement class has two properties

    • Name: For holding descriptive name for the core assembly and
    • Assembly: For holding name of the assembly we want NHSessionManager to load.

     
    
    
     public class NHAssemblyElement : ConfigurationElement
    {
      public NHAssemblyElement( )
      { 
    
    
      } 
    
    
      public NHAssemblyElement( string name, string assembly )
      {
        Name = name;
        Assembly = assembly;
      } 
    
    
      [ConfigurationProperty("name", IsRequired = true, IsKey = true, DefaultValue = "Not Supplied")]
      public string Name
      {
        get { return (string)this["name"]; }
        set { this["name"] = value; }
      } 
    
    
      [ConfigurationProperty("assembly", IsRequired = true, DefaultValue = "Not Supplied")]
      public string Assembly
      {
        get { return (string)this["assembly"]; }
        set { this["assembly"] = value; }
      }
    } 
    
    
    

    NHAssembliesCollection

    NHAssembliesCollection is inherited from standardConfigurationElementCollection class.

     
    
    
    [ConfigurationCollection(typeof(NHAssemblyElement))]
    public class NHAssembliesCollection : ConfigurationElementCollection
    {
      public NHAssembliesCollection( )
      {
        NHAssemblyElement element = (NHAssemblyElement)CreateNewElement();
        Add(element);
      } 
    
    
      public override ConfigurationElementCollectionType CollectionType
      {
        get
        {
          return ConfigurationElementCollectionType.AddRemoveClearMap;
        }
      } 
    
    
      protected override ConfigurationElement CreateNewElement( )
      {
        return new NHAssemblyElement();
      } 
    
    
      protected override object GetElementKey( ConfigurationElement element )
      {
        return ((NHAssemblyElement)element).Name;
      } 
    
    
      public NHAssemblyElement this[int index]
      {
        get
        {
          return (NHAssemblyElement)BaseGet(index);
        }
        set
        {
          if (BaseGet(index) != null)
          {
            BaseRemoveAt(index);
          } 
    
    
          BaseAdd(index, value);
        }
      } 
    
    
      new public NHAssemblyElement this[string name]
      {
        get
        {
          return (NHAssemblyElement)BaseGet(name);
        }
      } 
    
    
      public int IndexOf( NHAssemblyElement assembly )
      {
        return BaseIndexOf(assembly);
      } 
    
    
      public void Add( NHAssemblyElement assembly )
      {
        BaseAdd(assembly);
      } 
    
    
      protected override void BaseAdd( ConfigurationElement element )
      {
        BaseAdd(element, false);
      } 
    
    
      public void Remove( NHAssemblyElement assembly )
      {
        if (BaseIndexOf(assembly) >= 0)
        {
          BaseRemove(assembly.Name);
        }
      } 
    
    
      public void RemoveAt( int index )
      {
        BaseRemoveAt(index);
      } 
    
    
      public void Remove( string name )
      {
        BaseRemove(name);
      } 
    
    
      public void Clear( )
      {
        BaseClear();
      }
    } 
    
    
    

    NHAssembliesSection

    NHAssembliesSection inherits from standard ConfigurationSection and implements the section handler specified in the configuration file as

     
    
    
    <section name="nHibernateAssemblies" type=" NHibernateSample.Common.Configuration.NHAssembliesSection, NHibernateSample.Common"/> 
    
    
    
     

    The code is

     
    
    
    public class NHAssembliesSection : ConfigurationSection
    {
      [ConfigurationProperty("NhAssemblies", IsDefaultCollection = false)]
      [ConfigurationCollection(typeof(NHAssembliesCollection), AddItemName = "NhAssembly", ClearItemsName = "clearAssemblies")]
      public NHAssembliesCollection NHAssemblies
      {
       get
       {
        NHAssembliesCollection assembliesCollection = (NHAssembliesCollection)base["NhAssemblies"];
        return assembliesCollection;
       }
      }
    } 
    
    
    

    Little modification to NHSessionManager

    In NHSessionManager we have to change InitSessionFactory() method a little bit. Original version provided by Bill McCafferty implements loading of NHibernate assemblies with the following code

     
    
    
    private void InitSessionFactory() 
    {
     NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
     // The following makes sure the the web.config contains a declaration for the HBM_ASSEMBLY appSetting
     if (ConfigurationManager.AppSettings["HBM_ASSEMBLY"] == null || 
         ConfigurationManager.AppSettings["HBM_ASSEMBLY"] == "") 
     {
      throw new ConfigurationErrorsException("NHibernateManager.InitSessionFactory: \"HBM_ASSEMBLY\" must be " +
        "provided as an appSetting within your config file. \"HBM_ASSEMBLY\" informs NHibernate which assembly " +
        "contains the HBM files. It is assumed that the HBM files are embedded resources. An example config " +
        "declaration is \" key="\" />");
     }
     
     cfg.AddAssembly(System.Configuration.ConfigurationManager.AppSettings["HBM_ASSEMBLY"]);
     sessionFactory = cfg.BuildSessionFactory();
    }
    
    

    The code above handles configuration file as the following one

     
    
    
    <appSettings>
     <add key="HBM_ASSEMBLY" value="NHibernateSample.Core"/>
    </appSettings> 
    
    
    

    As I mentioned in the introduction of the article with this NHSessionManager implementation we have to change NHSessionManager source code and recompile every time we want to handle another NHibernate assembly.

    Our alternative InitSessionFactory implementaion of NHSessionManager code looks like following

    
    
    private void InitSessionFactory( )
    {
      NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();
    
    
      NHAssembliesSection nhAssembliesSection = ConfigurationManager.GetSection("nHibernateAssemblies") as NHAssembliesSection;
      
      if(nhAssembliesSection == null)
      {
        throw new ConfigurationErrorsException("nHibernateAssemblies section not defined in application configuration!");
      }
    
    
      foreach(NHAssemblyElement element in nhAssembliesSection.NHAssemblies)
      {
        try
        {
          cfg.AddAssembly(element.Assembly);
        }
        catch (Exception e)
        {
          throw new Exception(" Can not add assambly! " + e.Message, e.InnerException);
        }
      }
    
    
      try
      {
        sessionFactory = cfg.BuildSessionFactory();
      }
      catch (Exception e)
      {
        throw new Exception(" Can not create session factory! " + e.Message, e.InnerException);
      }
    } 
    
    
    


    Posted in: .NET Development , CodeProject , NHibernate  Tags:

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5

    Introduction

    In previous CodeProject.com 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 implemetation 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. Data project contains reference to Core project that is why we can not add
    Data project as reference to core project, this will cause circular reference and .NET does not allow us to use
    cricular 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

    This two methods causes presentation layer developers (WinForm or Web) to write somet repetative code like this one

     
    
    
    public void DoSomething()
    {
      // DaoFactory can be stored as variable in session (Web) or as a static variable (WinForm)
      IDaoFactory daoFactory = new DaoFactory();
      
      // This can also be stored as variable in session (Web) or as a static variable (WinForm)
      ISomeDomainObjectDao dao = daoFactory.GetSomeDomainObjectDao();
      
      //Method-1: Pass dao instance as a parameter in constructor
      SomeDomainObject domObj = new SomeDomainObject(dao); 
    
    
      //Method-2: Set DAO property of the domain object with an instance of the 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 described how we can achieve this.

    Castle MikroKernel/Windsor

    Castle MikroKernel is inversion of control container that provides us with very rich set of extensibility.
    You can find out more information about Castle/MikroKernel here
    In order to work with Castle MikroKernel

    1) We need three assemblies from Castel Project

    • Castle.
    • Castle.Windsor
    • Castle.MikroKernel

    2) 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 3 classes and 1 interface in this sample implementation.
    • DomainObject: This is our domain object that needs a DAO instance to query the database
    • IDaoFactory:Specifies which supported DAO objects 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

     
    
    
    /******************************************************************************
      File  : DaoFactoryExposer.cs
      Class : DaoFactoryExposer
      Description: 
    
    
      Created By: Ali Özgür
      Created On: 31/01/2007 14:55:24
      Contact   : ali_ozgur@hotmail.com 
    
    
      Revisions:
    *******************************************************************************/ 
    
    
    using System;
    using System.Collections.Generic;
    using System.Text; 
    
    
    using Castle.Windsor;
    using Castle.Windsor.Configuration.Interpreters; 
    
    
    
    namespace Bilgi.SampleProject.Core
    {
      /// 
      /// This class exposes dao factory using Castle.Windsoe IoC 
      /// container.
      /// 
      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 //Dao related
       
        //Some method that needs SomeNeededDao instance
        public void AddOrder(Customer customer, Product product)
        {
          // Here SomeNeededDao instance is created automatically by using DaoFactoryExposer
          decimal customerBalance = SomeNeededDao.QueryCustomerBalance(customer);
          if(customerBalance <= 0 )
          {
            throw new Exception("Not enough balance!");
          }
        }
        
      }
    } 
    
    
    


    Posted in: .NET Development , CodeProject , NHibernate  Tags:

    Be the first to rate this post

    • Currently 0/5 Stars.
    • 1
    • 2
    • 3
    • 4
    • 5