Skip to content

NHibernate Facility Quick Start

rarous edited this page Apr 30, 2013 · 3 revisions

In your App_Start or similar entry point to your application, create a new WindsorContainer:

using Castle.Facilities.NHibernate,

// ...

private WindsorContainer GetWindsorContainer()
{
	var c = new WindsorContainer();
	c.AddFacility<AutoTxFacility>();
	c.Register(Component.For<INHibernateInstaller>().ImplementedBy<ExampleInstaller>());	
	c.AddFacility<NHibernateFacility>();
	return c;
}

The NHibernate Facility will install all of its requirements.

Your NHibernate Installer implements this interface:

using System.Diagnostics.Contracts;
using Castle.Services.Transaction;
using FluentNHibernate.Cfg;
using NHibernate;

/// <summary>
/// 	Register a bunch of these; one for each database.
/// </summary>
[ContractClass(typeof(INHibernateInstallerContract))]
public interface INHibernateInstaller
{
	/// <summary>
	/// 	Is this the default session factory
	/// </summary>
	bool IsDefault { get; }

	/// <summary>
	/// </summary>
	string SessionFactoryKey { get; }

	/// <summary>
	/// An interceptor to assign to the ISession being resolved through this session factory.
	/// </summary>
	Maybe<IInterceptor> Interceptor { get; }

	/// <summary>
	/// Build a fluent configuration.
	/// </summary>
	/// <returns>A non null fluent configuration instance that can
	/// be used to further configure NHibernate</returns>
	FluentConfiguration BuildFluent();

	/// <summary>
	/// Call-back to the installer, when the factory is registered
	/// and correctly set up in Windsor..
	/// </summary>
	/// <param name="factory"></param>
	void Registered(ISessionFactory factory);
}

an example implementation looks like this:

internal class ExampleInstaller : INHibernateInstaller
{
	private readonly Maybe<IInterceptor> _Interceptor;

	public ExampleInstaller()
	{
		_Interceptor = Maybe.None<IInterceptor>();
	}

	public ExampleInstaller(IInterceptor interceptor)
	{
		_Interceptor = Maybe.Some(interceptor);
	}

	public Maybe<IInterceptor> Interceptor
	{
		get { return _Interceptor; }
	}

	public bool IsDefault
	{
		get { return true; }
	}

	public string SessionFactoryKey
	{
		get { return "sf.default"; }
	}

	public FluentConfiguration BuildFluent()
	{
		return Fluently.Configure()
			.Database(MsSqlConfiguration.
                                MsSql2008.
                                DefaultSchema("dbo").
                                ConnectionString(c => c.FromConnectionStringWithKey("test")))
			.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ThingMap>());
	}

	public void Registered(ISessionFactory factory)
	{
	}
}

Thing looks like this:

using System;

// ...

public class Thing
{
	[Obsolete("NHibernate's c'tor")]
	protected Thing()
	{
	}

	public Thing(double val)
	{
		Value = val;
	}

	public Guid ID { get; protected set; }
	public double Value { get; set; }
}

and ThingMap like this:

using FluentNHibernate.Mapping;

// ...

public class ThingMap : ClassMap<Thing>
{
	public ThingMap()
	{
		Not.LazyLoad();
		Id(x => x.ID).GeneratedBy.GuidComb();
		Map(x => x.Value).Column("val");
	}
}

A possible usage would now be a service that saves a new Thing:

[Test]
public void SavingWith_Transient_Lifestyle()
{
	// in your app_start:
	c.Register(Component.For<ServiceUsingTransientSessionLifestyle>().LifeStyle.HybridPerTransactionTransient());

	// your controller calls:
	using (var scope = new ResolveScope<ServiceUsingTransientSessionLifestyle>(c))
	{
		scope.Service.SaveNewThing();
		Assert.That(scope.Service.VerifyThing(), Is.Not.Null);
	}
}

// ... and the service would have this code:

// fields
private readonly Func<ISession> _Session;
// constructor:
public TearDownService(Func<ISession> session)
{
	Contract.Requires(session != null);
	_Session = session;
}

// and service-method
[Transaction]
public virtual void SaveNewThing()
{
	_Logger.DebugFormat("save new thing");

	using (var session = _GetSession())
	{
		// at KTH this is an arbitrary number
		_Id = (Guid) session.Save(new Thing(17.0));

		_Logger.DebugFormat("exiting using-block of session");
	}
}

Want logging? In your entry point:

using log4net;
// ...
XmlConfigurator.Configure();

and in your App.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
	</configSections>
	<connectionStrings>
		<add name="test" connectionString="Data Source=.;Initial Catalog=TxTests;Integrated Security=SSPI"/>
	</connectionStrings>
	<log4net debug="false">
		<appender name="DebugAppender" type="log4net.Appender.DebugAppender">
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%date [%thread] %-5level %logger [%ndc] - %message%newline"/>
			</layout>
		</appender>
		<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%date [%thread] %-5level - %message%newline"/>
			</layout>
		</appender>
		<root>
			<level value="DEBUG"/>
			<appender-ref ref="TraceAppender"/>
			<!--<appender-ref ref="DebugAppender"/>-->
		</root>
		<logger name="NHibernate">
			<level value="INFO"/>
		</logger>
		<logger name="NHibernate.Impl">
			<level value="DEBUG"/>
		</logger>
		<logger name="NHibernate.Engine.Loading">
			<level value="ERROR" />
		</logger>
		<logger name="Castle.Services.Transaction">
			<level value="DEBUG"/>
		</logger>
	</log4net>
	<!--<system.diagnostics>
		<sources>
			--><!--Verbose--><!--
			<source name="System.Transactions" switchValue="Information">
				<listeners>
					<add name="tx"
						type="Castle.Services.Transaction.Tests.TxTraceListener, Castle.Services.Transaction.Tests"/>
				</listeners>
			</source>
		</sources>
	</system.diagnostics>-->
</configuration>

Have fun!

Clone this wiki locally