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:
- The
ISession
's cache isn't working properly - 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:
MooseUserRepository
- a repository class for managing MooseUser instances (i.e. the page user in this case)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?
I've worked out what the problem is and it is quite a subtle one.
I'm retrieving the user with the following code:
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.