views:

154

answers:

4

I'm trying to work out how to complete my implementation of the Repository pattern in an ASP.NET web application.

At the moment, I have a repository interface per domain class defining methods for e.g. loading and saving instances of that class.

Each repository interface is implemented by a class which does the NHibernate stuff. Castle Windsor sorts out the DI of the class into the interface according to web.config. An example of an implemented class is provided below:

  public class StoredWillRepository : IStoredWillRepository
  {
    public StoredWill Load(int id)
    {
      StoredWill storedWill;
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        storedWill = session.Load<StoredWill>(id);
        NHibernateUtil.Initialize(storedWill);
      }
      return storedWill;
    }

    public void Save(StoredWill storedWill)
    {
      using (ISession session = NHibernateSessionFactory.OpenSession())
      {
        using (ITransaction transaction = session.BeginTransaction())
        {
          session.SaveOrUpdate(storedWill);
          transaction.Commit();
        }
      }
    }
  }

As pointed out in a previous thread, the repository class needs to accept an unit of work container (i.e. ISession) rather than instantiating it in every method.

I anticipate that the unit of work container will be created by each aspx page when needed (for example, in a property).

How do I then specify that this unit of work container instance is to be passed into the constructor of StoredWillRepository when Windsor is creating it for me?

Or is this pattern completely wrong?

Thanks again for your advice.

David

A: 

See this article for a full explanation of how to provide sessions to concrete repository implementations using castle.

DanP
Thanks, I'm having a good look, but digestion will take some time.
David
A: 

I have a pretty similar structure to yours, and here's how I solve your question:

1) To specify my container on each method, I have a separate class ("SessionManager") which I then invoke via a static property. By doing so, here's an example using my Save implementation:

private static ISession NHibernateSession
{
    get { return SessionManager.Instance.GetSession(); }
}

public T Save(T entity)
{
    using (var transaction = NHibernateSession.BeginTransaction())
    {
        ValidateEntityValues(entity);
        NHibernateSession.Save(entity);

        transaction.Commit();
    }

    return entity;
}

2) My container is not created on each ASPX page. I instantiate all of my NHibernate goodness on the global.asax page.

** A few more things spring up **

3) You don't need to have a helper to instantiate the Load. You might as well use Get instead of Load. More information @ Difference between Load and Get.

4) Using your current code, you would have to repeat pretty much the same code for each domain object you need (StoredWillRepository, PersonRepository, CategoryRepository, etc..?), which seems like a drag. You could very well use a generic class to operate over NHibernate, like:

public class Dao<T> : IDao<T>
{
    public T SaveOrUpdate(T entity)
    {
        using (var transaction = NHibernateSession.BeginTransaction())
        {
            NHibernateSession.SaveOrUpdate(entity);
            transaction.Commit();
        }

        return entity;
    }
}

In my implementation, I could then use something like:

Service<StoredWill>.Instance.SaveOrUpdate(will);
Rafael Belliard
Thank you very much for your input. I'll respond to your points in numbered comments below:
David
1) A static ISession? Unless I've misunderstood, that would mean the ISession (which is not thread safe) is shared between all threads of the application - very dangerous. Is this not correct?
David
2) Container for what? Sorry, I don't see what you mean.
David
3) In the code I posted, I used NHibernate.Util to pre-load the entity. Notice the Session is created and destroyed in the method call. This means the returned entity would be outside the Session, so when it tries to lazy load, an exception is thrown. Since I'm now passing an ISession in to the factory, I don't have to preload the entity as it will lazy load normally.
David
4) Good point. I'm just not very good at generics - yet.
David
Assuming that the GetSession() method on SessionManager implements locking, then static is OK (although it will reduce performance in a multi-threaded context; unless of course the underlying storage is marked [ThreadStatic]). In my own similar implementation I use a similar trick to enable session-per-thread (and indeed per-UOW) with static access semantics.
DotNetGuy
Apparently ThreadStatic is not a good idea in ASP.NET: http://www.hanselman.com/blog/StoringThingsOnThreads.aspx
David
Indeed. When working in a web context I store stuff in HttpContext.Current.Items, which gives you per-thread storage that is thread-pool safe.
DotNetGuy
@David 1) In all honesty, the SessionManager I use is a slightly modified version of the 'thread-safe, lazy singleton patterned NHibernateSessionManager described in the NHibernate Best Practices Article.' (http://bit.ly/aLL5Ia). I know of several people who use it (Google it) and I've been using it for the past 18 months without major hiccups. I would prefer you read that article instead of me trying to explain it in 3 lines of comment. :D
Rafael Belliard
@DotNetGuy: I notice this implementation also uses the HttpContext - http://www.codepaste.net/fx6quo :)
Rafael Belliard
Yeah, it's pretty common practice, I think. Not that there aren't lots of other fun ways to do it too :-)
DotNetGuy
@David 3) What I'm trying to understand is if you'd rather throw an exception (and handle) when an entity is not found using Load, rather than using Get, which returns null if the entity is not found?
Rafael Belliard
@David 2) I misunderstood something. 8a.m... Skip that. :)
Rafael Belliard
@Rafael 3) I like exceptions. You can't miss them. Thank you for all your help. I'm really having to think these things through right now.
David
+1  A: 

I have a persistence framework built on top of NHibernate that is used in a few Web apps. It hides the NH implementation behind an IRepository and IRepository<T> interface, with the concrete instances provided by Unity (thus I could in theory swap out NHibernate for, say, Entity Framework fairly easily).

Since Unity doesn't (or at least the version I'm using doesn't) support the passing in of constructor parameters other than those that are dependency injections themselves, passing in an extant NH ISession isn't possible; but I do want all objects in the UOW to share the same ISession.

I solve this by having a controlling repository class that manages access to the ISession on a per-thread basis:

    public static ISession Session
    {
        get
        {
            lock (_lockObject)
            {
                // if a cached session exists, we'll use it
                if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                {
                    return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY];
                }
                else
                {
                    // must create a new session - note we're not caching the new session here... that's the job of
                    // BeginUnitOfWork().
                    return _factory.OpenSession(new NHibernateInterceptor());
                }
            }
        }
    }

In this example, PersistenceFrameworkContext.Current.Items accesses an IList<object> that is stored either ThreadStatic if not in a Web context, or within HttpContext.Current.Items if it is in a Web context (to avoid thread-pool problems). The first call to the property instantiates the ISession from the stored factory instance, subsequent calls just retrieve it from storage. The locking will slow things down slightly but not as much as just locking an appdomain-scoped static ISession instance.

I then have BeginUnitOfWork and EndUnitOfWork methods to take care of the UOW - I have specifically disallowed nested UOWs because frankly they were a pain to manage.

    public void BeginUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
                EndUnitOfWork();

            ISession session = Session;
            PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session);
        }
    }

    public void EndUnitOfWork()
    {
        lock (_lockObject)
        {
            if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY))
            {
                ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY];
                PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY);
                session.Flush();
                session.Dispose();
            }
        }
    }

Finally, a pair of methods provide access to the domain-type-specific repositories:

    public IRepository<T> For<T>()
        where T : PersistentObject<T>
    {
        return Container.Resolve<IRepository<T>>();
    }

    public TRepository For<T, TRepository>()
        where T : PersistentObject<T>
        where TRepository : IRepository<T>
    {
        return Container.Resolve<TRepository>();
    }

(Here, PersistentObject<T> is a base class providing ID and Equals support.)

Access to a given repository is thus in the pattern

NHibernateRepository.For<MyDomainType>().Save();

This is then facaded over such that you can use

MyDomainType.Repository.Save();

Where a given type has a specialised repository (ie needs more than it can get from IRepository<T>) then I create an interface deriving from IRepository<T>, an extending implementation inheriting from my IRepository<T> implementation, and in the domain type itself I override the static Repository property using new

    new public static IUserRepository Repository
    {
        get
        {
            return MyApplication.Repository.For<User, IUserRepository>();
        }
    }

(MyApplication [which is called something less noddy in the real product] is a facade class which takes care of supplying the Repository instance via Unity so you have no dependency on the specific NHibernate repository implementation within your domain classes.)

This gives me full pluggability via Unity for the repository implementation, easy access to the repository in code without jumping through hoops, and transparent, per-thread ISession management.

There's lots more code than just what's above (and I've simplified the example code a great deal), but you get the general idea.

MyApplication.Repository.BeginUnitOfWork();
User user = User.Repository.FindByEmail("[email protected]");
user.FirstName = "Joe"; // change something
user.LastName = "Bloggs";
// you *can* call User.Repository.Save(user), but you don't need to, because...
MyApplication.Repository.EndUnitOfWork();
// ...causes session flush which saves the changes automatically

In my Web app, I have session-per-request, so BeginUnitOfWork and EndUnitOfWork get called in BeginRequest and EndRequest respectively.

DotNetGuy
That's awesome. I'll definitely be checking out if my code could use some of yours' goodness. xD
Rafael Belliard
Stack Overflow needs a button for 'Probably the right answer, but to be frank I don't understand it'. I'd be clicking it right here. I will try to digest it all over the weekend but it may come second best to getting drunk. At least I appreciated your email address!
David
Getting drunk is an excellent idea which I shall be copying later this evening :-) As for the code, all the levels of indirection and metaprogramming does make for a harder read. The real and full implementation of our persistence framework is about the most complex code in our arsenal here - but once you've done it once, you never need to do it again. It's paid for itself many times over.
DotNetGuy
A: 

Technically, the answer to my question is to use the overload of container.Resolve which allows you to specify the constructor argument as an anonymous type:

IUnitOfWork unitOfWork = [Code to get unit of work];
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork });

But let's face it, the answers provided by everyone else have been much more informative.

David