Introduction
In a 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 the sake of code reuse, we can think of reorganising the NHibernate
utility classes (NHSessionManager
, DomainObject
, NHGenericDao
) and interfaces (IGenericDao
) in a separate 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 of how to extend the configuration system of .NET.
Extending Configuration System
We need a configuration section handler and configuration element specification in order to specify multiple NHibernate
assemblies 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.
The NHAssemblyElement class has two properties:
Name
for holding descriptive name for the core assembly
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 the standard ConfigurationElementCollection
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 the 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 as follows:
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 the InitSessionFactory()
method a little bit.
The 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();
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 a 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
implementation of NHSessionManager
code looks like the 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 assembly! " + e.Message, e.InnerException);
}
}
try
{
sessionFactory = cfg.BuildSessionFactory();
}
catch (Exception e)
{
throw new Exception(" Can not create session factory! " +
e.Message, e.InnerException);
}
}
History
- 1st February, 2007: Initial post