Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing UnitOfWork with Castle.Windsor

Simple question.

How do I use UnitOfWork with Castle.Windsor, nHibernate, and ASP.NET MVC?

Now for the extended details. In my quest to understand the UnitOfWork pattern, I'm having difficulty coming across anything that uses a direct example in conjunction with Castle.Windsor, specifically in regards to the way it needs to be installed.

Here is my understanding so far.

IUnitOfWork

  • The IUnitOfWork interface is used to declare the pattern
  • The UnitOfWork class must Commit and Rollback transactions, and Expose a Session.

So with that said, here is my IUnitOfWork. (I am using Fluent nHibernate)

public interface IUnitOfWork : IDisposable
{
    ISession Session { get; private set; }
    void Rollback();
    void Commit();
}

So here is my Castle.Windsor Container Bootstrapper (ASP.NET MVC)

public class WindsorContainerFactory
{
    private static Castle.Windsor.IWindsorContainer container;
    private static readonly object SyncObject = new object();

    public static Castle.Windsor.IWindsorContainer Current()
    {
        if (container == null)
        {
            lock (SyncObject)
            {
                if (container == null)
                {
                    container = new Castle.Windsor.WindsorContainer();

                    container.Install(new Installers.SessionInstaller());
                    container.Install(new Installers.RepositoryInstaller());
                    container.Install(new Installers.ProviderInstaller());
                    container.Install(new Installers.ControllerInstaller());
                }
            }

        }

        return container;
    }
}

So now, in my Global.asax file, I have the following...

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        // Register the Windsor Container
        ControllerBuilder.Current
            .SetControllerFactory(new Containers.WindsorControllerFactory());
    }

Repository

Now I understand that I need to pass the ISession to my Repository. So then, let me assume IMembershipRepository.

class MembershipRepository : IMembershipRepository
{
   private readonly ISession session;
   public MembershipRepository(ISession session)
   {
      this.session = session;
   }

   public Member RetrieveMember(string email)
   {
      return session.Query<Member>().SingleOrDefault( i => i.Email == email );
   }
}

So I am confused, now. Using this method, the ISession doesn't get destroyed properly, and the UnitOfWork never gets used.

I've been informed that UnitOfWork needs to go in the Web Request Level - but I cannot find anything explaining how to actually go about this. I do not use a ServiceLocator of any sort ( as when I tried, I was told this was also bad practice... ).

Confusion -- How does a UnitOfWork get created?

I just don't understand this, in general. My thought was that I would start passing UnitOfWork into the Repository constructors - but if it has to go in the Web Request, I'm not understanding where the two relate.

Further Code

This is extra code for clarification, simply because I seem to have a habit of never providing the right information for my questions.

Installers

public class ControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            AllTypes.FromThisAssembly()
            .BasedOn<IController>()
            .Configure(c => c.LifeStyle.Transient));
    }
}

public class ProviderInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component
            .For<Membership.IFormsAuthenticationProvider>()
            .ImplementedBy<Membership.FormsAuthenticationProvider>()
            .LifeStyle.Singleton
        );
    }
}

public class RepositoryInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component
            .For<Membership.IMembershipRepository>()
            .ImplementedBy<Membership.MembershipRepository>()
            .LifeStyle.Transient
        );

        container.Register(
            Component
            .For<Characters.ICharacterRepository>()
            .ImplementedBy<Characters.CharacterRepository>()
            .LifeStyle.Transient
        );
    }
}

public class SessionInstaller : Castle.MicroKernel.Registration.IWindsorInstaller
{
    private static ISessionFactory factory;
    private static readonly object SyncObject = new object();

    public void Install(Castle.Windsor.IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<ISessionFactory>()
                .UsingFactoryMethod(SessionFactoryFactory)
                .LifeStyle.Singleton
            );

        container.Register(
            Component.For<ISession>()
            .UsingFactoryMethod(c => SessionFactoryFactory().OpenSession())
            .LifeStyle.Transient
        );
    }

    private static ISessionFactory SessionFactoryFactory()
    {
        if (factory == null)
            lock (SyncObject)
                if (factory == null)
                    factory = Persistence.SessionFactory.Map(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Remote"].ConnectionString);
        return factory;
    }
}

UnitOfWork

Here is my UnitOfWork class verbatim.

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory sessionFactory;
    private readonly ITransaction transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
        Session = this.sessionFactory.OpenSession();
        transaction = Session.BeginTransaction();
    }

    public ISession Session { get; private set; }

    public void Dispose()
    {
        Session.Close();
        Session = null;
    }

    public void Rollback()
    {
        if (transaction.IsActive)
            transaction.Rollback();
    }

    public void Commit()
    {
        if (transaction.IsActive)
            transaction.Commit();
    }
}
like image 973
Ciel Avatar asked Dec 17 '10 18:12

Ciel


1 Answers

Your NH Session is a Unit of Work already http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx

So I'm not sure why you would ever need to abstract this out any further. (if anyone reading this answer know's why I would be happy to hear, I've honestly never heard of any reason why you would need to...)

I would implement a simple Session Per Request. I don't know how you would do that with Windsor since I've never used it, but with It's rather simple with StructureMap.

I wrap the structuremap factory to hold my session factory and inject the session into the repositories as required.

    public static class IoC
    {
        static IoC()
        {
            ObjectFactory.Initialize(x =>
            {
                x.UseDefaultStructureMapConfigFile = false;

                // NHibernate ISessionFactory
                x.ForSingletonOf<ISessionFactory>()
                 .Use(new SessionFactoryManager().CreateSessionFactory());

                // NHibernate ISession
                x.For().HybridHttpOrThreadLocalScoped()
                 .Use(s => s.GetInstance<ISessionFactory>().OpenSession());

                x.Scan(s => s.AssembliesFromApplicationBaseDirectory());
            });

            ObjectFactory.AssertConfigurationIsValid();
        }

        public static T Resolve<T>()
        {
            return ObjectFactory.GetInstance<T>();
        }

        public static void ReleaseAndDisposeAllHttpScopedObjects()
        {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        }
    }

In the global.asax file on Request_End I call the ReleaseAndDisposeAllHttpScopedObjects() method.

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            IoC.ReleaseAndDisposeAllHttpScopedObjects();
        }

So the session is opened when I call my first repository, and when the request is ended it's disposed of. The repositories have a constructor which takes ISession and assigns it to a property. Then I just resolve the repo like:

var productRepository = IoC.Resolve<IProductRepository>();

Hope that helps. There are many other ways of doing it, this is what works for me.

like image 152
Phill Avatar answered Jan 08 '23 11:01

Phill