Try fast search NHibernate

27 June 2009

NHibernate Configuration

Perhaps not so many people know in how many ways NH can be configured since NH2.0.0. In this post I’ll try to summarize some one before implement Loquacious configuration in NH3.0.0.

Xml (default)

The xml way is the most common used so only some little notes are needed here.

Inside the root node, hibernate-configuration, you can configure 3 “things”: the bytecode provider, the reflection optimizer usage and the session-factory.

The bytecode provider and the reflection optimizer can be configured only and exclusively inside the application config (app.config or web.config); outside application config the two configuration are ignored. If you need to configure the bytecode provider or the reflection optimizer the minimal configuration required, inside app.config is, for example:

    <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<
bytecode-provider type="null"/>
<
reflection-optimizer use="false"/>
</
hibernate-configuration>

As you can see the session-factory section is not present (for this reason I wrote “minimal”).

The session-factory configuration can be wrote inside or outside the app.config or even in both (inside and outside). The minimal configuration required is:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<
session-factory name="NHibernate.Test">
<
property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>
<
property name="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu
</property>
</
session-factory>
</
hibernate-configuration>

As you can see there are only two properties and, as is, you can’t use this configuration because there isn’t the connection string.

If you want you can write the minimal configuration inside your application config and merge/override it with the configuration wrote in an external file. I’m using this technique everywhere for tests purpose because test suite and production has some common configuration and some other different configuration (for example the current_session_context_class). The code to merge/override the configuration inside app.config with the configuration outside the app.config is:

var configuration = new Configuration()
.Configure()
.Configure(yourNhConfPath);

After this two lines you can continue configuring NHibernate by code (even using the method chaining).

Xml (custom configSections)

As usual in NHibernate, you can have a custom Schema for the hibernate-configuration section. The way is simple:

    <configSections>
<
section name="hibernate-configuration"
type="YourCompany.YourProduct.YourConfigurationSectionHandler, YourAssembly" />
</
configSections>

The restriction is :

  1. The section name must be “hibernate-configuration”.
  2. Your ConfigurationSectionHandler must implements IHibernateConfiguration.

The reason to write a custom ConfigurationSectionHandler, more than implement something related to Enterprise Library-Configuration Application Block, can be related with the need of some framework (your imagination may work here).

Xml : The mystery

In these year I saw :

  • everybody using property
  • somebody using the mapping section
  • few people using the event and/or the listener sections
  • absolutely nobody using the class-cache nor collection-cache sections

The two sections related with the second-level-cache configuration are very useful. In general the configuration of the cache is something happening after wrote all mappings; you may have a tests suite that must work without cache and another test suite to check the behavior using second-level-cache. Even if I have changed NH2.1.0 to ignore the cache configuration, inside a class-mapping, when the cache.use_second_level_cache is set to false, the right place to configure the cache, of each class/collection, is inside the session-factory-configuration and not inside the class-mapping itself; you don’t need to modify a tested mapping only because cache and you don’t need to re-deploy class-mappings only because you need to modify/add the cache configuration of a class/collection.

Configuration by code

The whole configuration via XML can be done by pure .NET code.

The bytecode provider can be set before create an instance of the Configuration class:

Environment.BytecodeProvider = new EnhancedBytecode(container);
var cfg = new Configuration();

To set properties in a no strongly typed way (mean by strings) :

configuration.SetProperty(Environment.GenerateStatistics, "true");
configuration.SetProperty(Environment.BatchSize, "10");

To set listeners in a no strongly typed way (mean by strings) :

configuration.SetListeners(ListenerType.PreInsert,
new[] { "YourCompany.YourProduct.YourListener, YourAssembly" });

To set listeners in strongly typed way :

configuration.EventListeners.DeleteEventListeners =
new[] {new ResetReadOnlyEntityDeleteListener()}
.Concat(listeners.DeleteEventListeners).ToArray();

To add class-mappings the Configuration class has various Add* methods :

AddAssembly(Assembly assembly)
AddAssembly(string assemblyName)
AddClass(System.Type persistentClass)
AddDirectory(DirectoryInfo dir)
AddFile(string xmlFile)
AddFile(FileInfo xmlFile)
AddXmlReader(XmlReader hbmReader)
AddXml(string xml)
AddResource(string path, Assembly assembly)
...
...

Note AddXml(string xml) or AddXmlString(string xml) mean that you can add a mapping created at run-time:

string hbm =
@"<?xml version='1.0' encoding='utf-8' ?>
<hibernate-mapping xmlns='urn:nhibernate-mapping-2.2'
namespace='NHibernate.DomainModel'
assembly='NHibernate.DomainModel'>
<class name='A' persister='A'>
<id name='Id'>
<generator class='native' />
</id>
</class>
</hibernate-mapping>"
;

Configuration cfg = new Configuration();
cfg.AddXmlString(hbm);

To set second-level-cache configuration for classes and collections:

SetCacheConcurrencyStrategy(string clazz, string concurrencyStrategy)
SetCacheConcurrencyStrategy(string clazz, string concurrencyStrategy, string region)
SetCollectionCacheConcurrencyStrategy(string collectionRole, string concurrencyStrategy)

The trick of custom Dialect

Few people know this trick (perhaps only the one I’m seeing everyday in the mirror).

Each Dialect has the ability to define its own DefaultProperties. Inside the core, so far, we are using the DefaultProperties property only for few purpose (for example to define the default driver):

DefaultProperties[Environment.ConnectionDriver] =
"NHibernate.Driver.SqlClientDriver";

If you are not so scared by inherit from a dialect implemented in the core and extends it to register some functions or to change some behavior, you can use it even to define default properties:

public class YourCustomDialect : MsSql2005Dialect
{
public YourCustomDialect()
{
DefaultProperties[Environment.QuerySubstitutions] =
"wahr 1, falsch 0, ja 'J', nein 'N'";
DefaultProperties[Environment.ConnectionString] =
"your connection string";
DefaultProperties[Environment.BatchSize] = "50";
}
}

What we saw

Since NH2.0.0GA, you have three or four ways to configure NHibernate with merge/override from different sources.

No comments:

Post a Comment