我刚刚读过我的一个ASP.NET页面的跟踪,并且我注意到每次需要用户时都会从数据库加载页面用户。由于每个ISession
都应该缓存对象,我对此真的很不满。
逻辑上,问题肯定是以下两件事之一:
ISession
的缓存无法正常运行ISession
我认为问题是数字2)。我使用Castle Windsor来管理对象生命周期,所以我发布了一些我用过的代码,以防有人帮助发现问题。由温莎城堡管理的课程是:
MooseUserRepository
- 用于管理MooseUser实例的存储库类(在本例中为页面用户)KctcUnitOfWork
- ISession的包装器 MooseUserRepository
对KctcUnitOfWork
有一个构造函数依赖,如下所示:
public MooseUserRepository(IUnitOfWork unitOfWork)
{
}
配置文件如下所示:
<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"/>
请注意PerWebRequest
生活方式。
Castle Windsor容器只是一种名为Moose.Application
的实用程序类的静态属性,所以它始终存在:
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;
}
}
页面本身有一个IMooseUserRepository实例,如下所示:
private IMooseUserRepository _mooseUserRepository;
private IMooseUserRepository MooseUserRepository
{
get
{
if (_mooseUserRepository == null)
{
_mooseUserRepository = Moose.Application.WindsorContainer.Resolve<IMooseUserRepository>();
}
return _mooseUserRepository;
}
}
页面的用户可以通过如下属性访问:
private MooseUser PageUser
{
get { return MooseUserRepository.Load(ApplicationSettings.UsernameFromWeb); }}
对PageUser
的这些后续调用似乎导致了重复的SQL命令:
txtSubject.Enabled = PageUser.CanHandleLegalWorks;
ddlDue.Enabled = PageUser.CanHandleLegalWorks;
现在很明显我可以通过将加载的MooseUser
对象存储在私有变量中来解决这个问题,但我的理解是ISession
应该为我做这个。
任何人都可以猜测出了什么问题吗?
答案 0 :(得分:2)
您声明以下内容:
逻辑上,问题肯定是 以下两件事之一:
- ISession的缓存无法正常工作
- 每次请求用户时,都会使用
醇>
加载 不同的ISession
我认为你可能会将Nhibernate的First(Session)级别缓存与二级缓存混淆。
会议制造和销售都很便宜。在Web应用程序中,通常每个请求使用一个会话。一旦您第一次获得或加载实体,它将被放入第一级缓存,该缓存的范围限定为会话的生命周期。当会话在请求结束时关闭并处理时,您将无法再访问会话级缓存中的对象。实际上,每个用户都被不同的会话加载 - 这是完全正常的。
二级缓存的范围限定为会话工厂的生命周期。如果启用了第二级缓存,则一旦通过其主键加载实体,它就会存储在二级缓存中,并且可以被所有会话访问,而不会再次访问数据库,直到从缓存中删除它为止。您需要在每个实体的基础上明确启用缓存。这是您正在寻找的行为。
进一步阅读:
修改的
您需要从NHContrib project.中选择一个缓存提供程序您可能需要使用Asp.Net缓存的SysCache2,但如果您愿意,可以使用MemCached或Velocity或其他几个缓存。 我还建议你试试Nhibernate Profiler。我发现它非常有价值,能够深入了解Nhibernate的内容。
答案 1 :(得分:1)
就像您在问题中注意到的那样,您正在为存储库使用PerWebRequest生活方式,因此将在每个请求上重新创建ISession(由存储库使用)。 我发现这种行为是正确的,应该在每个请求上创建ISession,并且应该处理NH上的每个操作。
如果您想将一个ISession用作单身人士,则应将存储库生活方式声明为单身人士。
我认为你的应用程序中应该有一些SessionProvider或SessionFactory,也许你可以为单例会话工作。
HTH
答案 2 :(得分:1)
我已经解决了问题所在,这是一个非常微妙的问题。
我正在使用以下代码检索用户:
private MooseUser PageUser
{
get { return MooseUserRepository.Load(ApplicationSettings.UsernameFromWeb); }
}
就ASP.NET而言, ApplicationSettings.UsernameFromWeb
检索当前用户的用户名。用户的用户名是Users表的自然键,但它不是主键!据我所知,第一级缓存仅适用于由主键检索的对象。
修改强> 我通过创建一个属性来解决这个问题,该属性将加载的用户填入HttpContext.Current.Items并在根据this文章加载之前先检查它。
答案 3 :(得分:1)
您可以在映射中使用natural-id(Fluent NH中的NaturalId)来绕过这种情况绕过二级缓存过期。