I am trying to use the Windsor NHibernate Facility for the first time in a Wcf service and replace the current manual registration of NHibernate so that there can be a consistent approach across all services.

Current working approach

Previously I had been registering the NHibernate components manually.

container.Register(
    Component.For<ISessionFactory>().UsingFactoryMethod(() => CreateMappings("SomeConnectionString").BuildSessionFactory()));
container.Register(
    Component.For<ISession>().LifeStyle.PerWcfOperation().UsingFactoryMethod(OpenSession));

I was then using a custom service behavior to create and complete a transaction scope for each operation.

public class TransactionBehavior : IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var cdb in serviceHostBase.ChannelDispatchers)
        {
            var channelDispatcher = cdb as ChannelDispatcher;

            if (null == channelDispatcher) continue;

            foreach (var endpointDispatcher in channelDispatcher.Endpoints)
            {
                foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
                {
                    dispatchOperation.CallContextInitializers.Add(new TransactionContext());
                }
            }
        }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { }
}

public class TransactionContext : ICallContextInitializer
{
    private TransactionScope transaction;

    public Object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
    {
        transaction = new TransactionScope();
        return null;
    }

    public void AfterInvoke(Object correlationState)
    {
        if(transaction != null)
        {
        transaction.Complete();
        transaction.Dispose();
        }
    }
}

Integration of the NHibernate Facility

I have downloaded the 0.3.1.2001 artifacts and using the resources below I have tried to plug in the facility and remove any manual wire up to NHibernate. However I do not want to decorate services and methods with the Transactional and Transaction attributes. The following is my current wire up.

container.Register(Component.For<INHibernateInstaller>().ImplementedBy<MyNHibernateInstaller>();
container.AddFacility<AutoTxFacility>();
container.AddFacility<NHibernateFacility>();

public class MyNHibernateInstaller : INHibernateInstaller
{
    public Maybe<IInterceptor> Interceptor { get { return Maybe.None<IInterceptor>(); } }

    public bool IsDefault { get { return true; } }

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

    public FluentConfiguration BuildFluent()
    {
        return Fluently
            .Configure()
            .Database(MsSqlConfiguration
                .MsSql2005.ConnectionString("SomeConnectionString") )
                .Mappings( m => m.FluentMappings
                    .AddFromAssemblyOf<TypeFromEntityAssembly>() );
    }

    public void Registered(ISessionFactory factory)
    {
    }
}

Whenever I call one of the service endpoints the service fails with the following exception:

No transaction in context when trying to instantiate model 'NHibernate.ISession' for resolve type 'Juice.iCheque.eMoneySystem.Settlement.ISettlementService'. If you have verified that your call stack contains a method with the [Transaction] attribute, then also make sure that you have registered the AutoTx Facility.

The question is how do I use the NHibernateFacility with my current implementation and not use the Transaction attribute.

Resources

http://richarddingwall.name/2010/08/17/one-nhibernate-session-per-wcf-operation-the-easy-way/

https://github.com/haf/Castle.Facilities.NHibernate/wiki/NHibernate-Facility---Quick-Start

1

There are 1 best solutions below

0
On

Firstly you should know that Henrik Feldt's Castle Transactions and AutoTx Facility interpretations require [Transaction] attribute usage (as I have discussed with Mr.Feldt in Castle Users mailing lists some time ago).

If you want to get rid of the [Transaction] attribute from your service classes (to achive POCO) but continue to use NH and WCF per call session scenario you should:

  1. Keep your IServiceBehaviour implementation but inject an IDispatchMessageInspector interface at the constructor, and assign it to a class global IDispatchMessageInspector property.

  2. At ApplyDispatchBehavior method foreach ChannelDispatcher in serviceHostBase, loop endpoints and add injected DispatchMessageInspector instance:

    foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
    {
        foreach (var endpoint in channelDispatcher.Endpoints)
        {
            endpoint.DispatchRuntime.MessageInspectors.Add(this.dispatchMessageInspector);
        }
    }
    
  3. Create a class implementing IDispatchMessageInspector (the one that would be injected at the previous item). Lets call this class NHWcfIntegrationMessageInspector.

  4. Inject ISessionFactory to NHWcfIntegrationMessageInspector, and set it to local property.

  5. In AfterRecieveRequest method create an NH session object with ISessionFactory.Open(), begin transaction and bind that ISession to CurrentSessionContext

  6. In BeforeSendReply method unbind injected SessionFactory from CurrentSessionContext, which gives you the actual session object. Check transaction is active from session's Transaction property and if active Commit() it. Then Dispose() session.

  7. Dont use INHibernateInstaller. Instead use IWindsorInstaller and implement it with MyHibernateInstaller. At registration block, register both your IServiceBehavior implementation and your new IDispatchMessageInspector implementation to container.

  8. Also register ISessionFactory in same installer and give FNH configuration method as a factory method but give a builded ISessionFactory. But at your FNH config before building ISessionFactory from fluentconfiguration object call:

    fluentConfiguration.CurrentSessionContext<WcfOperationSessionContext>();

  9. Ta DAAA!

After completing these steps, you dont have to hold references to Haf's libraries anymore. Its pure Castle.Core, Castle.Windsor, NH/FNH.

Any questions? ;)

(sorry for not posting the complete code but just description, I am under contract obligation)