Search code examples
nhibernatecastle-windsorisessionfirst-level-cache

NHibernate is loading same object multiple times - please help!


I've just been reading the trace for one of my ASP.NET pages and I've noticed that the page user is being loaded from the database each time the user is required. Since each ISession is supposed to cache objects, I'm really mystified about this.

Logically, the problem must surely be one of the following two things:

  1. The ISession's cache isn't working properly
  2. Each time the user is requested, it's being loaded using a different ISession

I assume that the problem is number 2). I'm using Castle Windsor to manage object lifecycles so I've posted some of the code I'm using in case someone can help spot the problem. The classes being managed by Castle Windsor are:

  1. MooseUserRepository - a repository class for managing MooseUser instances (i.e. the page user in this case)
  2. KctcUnitOfWork - a wrapper for the ISession

MooseUserRepository has a constructor dependency on KctcUnitOfWork like this:

public MooseUserRepository(IUnitOfWork unitOfWork)
    {

    }

The config file looks like this:

<component id="KctcUnitOfWork" service="Kctc.BusinessLayer.Kctc.IUnitOfWork,Kctc.BusinessLayer" type="Kctc.NHibernate.Kctc.UnitOfWork,Kctc.NHibernate" lifestyle="PerWebRequest"/>
<component id="MooseUserRepository" service="Kctc.BusinessLayer.Kctc.Repositories.IMooseUserRepository,Kctc.BusinessLayer" type="Kctc.NHibernate.Kctc.Repositories.MooseUserRepository,Kctc.NHibernate" lifestyle="PerWebRequest"/>

Note the PerWebRequest lifestyles.

The Castle Windsor container is simply a static property of a sort of utility class called Moose.Application so it's always there:

private static IWindsorContainer _windsorContainer;

    public static IWindsorContainer WindsorContainer
    {
      get
      {
        if (_windsorContainer == null)
        {
          _windsorContainer = new WindsorContainer(new XmlInterpreter(HttpContext.Current.Server.MapPath("~/CastleWindsorConfiguration.xml")));
        }
        return _windsorContainer;
      }
    }

The page itself has a IMooseUserRepository instance like this:

private IMooseUserRepository _mooseUserRepository;
private IMooseUserRepository MooseUserRepository
  {
    get
    {
      if (_mooseUserRepository == null)
      {
        _mooseUserRepository = Moose.Application.WindsorContainer.Resolve<IMooseUserRepository>();
      }
      return _mooseUserRepository;
    }
  }

The user of the page is accessed by a property which looks like this:

private MooseUser PageUser
  {
    get { return MooseUserRepository.Load(ApplicationSettings.UsernameFromWeb); }}

It appears that these subsequent calls to PageUser are causing the duplicate SQL commands:

txtSubject.Enabled = PageUser.CanHandleLegalWorks;
    ddlDue.Enabled = PageUser.CanHandleLegalWorks;

Now obviously I can work around this problem by storing the loaded MooseUser object in a private variable, but my understanding is that the ISession is supposed to do this for me.

Can anyone hazard a guess as to what's going wrong?


Solution

  • I've worked out what the problem is and it is quite a subtle one.

    I'm retrieving the user with the following code:

    private MooseUser PageUser
      {
        get { return MooseUserRepository.Load(ApplicationSettings.UsernameFromWeb); }
    }
    

    ApplicationSettings.UsernameFromWeb retrieves the username of the current user as far as ASP.NET is concerned. The username of the user is a natural key for the Users table, but it's not the primary key! As far as I know, the first level cache only works for objects retrieved by primary key.

    Edit: I solved this problem by creating a property which stuffs the loaded user in HttpContext.Current.Items and checks there first before loading as per this article.