NHibernate多次加载同一个对象 - 请帮忙!

时间:2011-01-07 15:08:23

标签: nhibernate castle-windsor isession first-level-cache

我刚刚读过我的一个ASP.NET页面的跟踪,并且我注意到每次需要用户时都会从数据库加载页面用户。由于每个ISession都应该缓存对象,我对此真的很不满。

逻辑上,问题肯定是以下两件事之一:

  1. ISession的缓存无法正常运行
  2. 每次请求用户时,都会使用其他ISession
  3. 加载用户

    我认为问题是数字2)。我使用Castle Windsor来管理对象生命周期,所以我发布了一些我用过的代码,以防有人帮助发现问题。由温莎城堡管理的课程是:

    1. MooseUserRepository - 用于管理MooseUser实例的存储库类(在本例中为页面用户)
    2. KctcUnitOfWork - ISession的包装器
    3. MooseUserRepositoryKctcUnitOfWork有一个构造函数依赖,如下所示:

      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应该为我做这个。

      任何人都可以猜测出了什么问题吗?

4 个答案:

答案 0 :(得分:2)

您声明以下内容:

  

逻辑上,问题肯定是   以下两件事之一:

     
      
  1. ISession的缓存无法正常工作
  2.   
  3. 每次请求用户时,都会使用
    加载   不同的ISession
  4.   

我认为你可能会将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)来绕过这种情况绕过二级缓存过期。