NHibernate загружает один и тот же объект несколько раз - пожалуйста, помогите!

Я только что прочитал трассировку для одной из моих страниц ASP.NET и заметил, что пользователь страницы загружается из базы данных каждый раз, когда пользователь требуется. Так как каждый ISession должен кешировать объекты, я действительно озадачен этим.

Логично, что проблема, безусловно, должна заключаться в одной из следующих двух вещей:

  1. ISessionкеш не работает должным образом
  2. Каждый раз, когда пользователь запрашивается, он загружается с использованием другого ISession

Я предполагаю, что проблема № 2). Я использую Castle Windsor для управления жизненными циклами объектов, поэтому я разместил часть кода, который я использую на тот случай, если кто-то может помочь выявить проблему. Классы, которыми управляет Замок Виндзор:

  1. MooseUserRepository - класс репозитория для управления экземплярами MooseUser (т.е. в этом случае пользователь страницы)
  2. 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 должен сделать это для меня.

Может ли кто-нибудь рискнуть догадаться о том, что идет не так?

4 ответа

Решение

Я понял, в чем проблема, и она довольно тонкая.

Я извлекаю пользователя с помощью следующего кода:

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

ApplicationSettings.UsernameFromWeb извлекает имя текущего пользователя в отношении ASP.NET Имя пользователя пользователя является естественным ключом для таблицы Users, но это не первичный ключ! Насколько я знаю, кэш первого уровня работает только для объектов, полученных по первичному ключу.

Редактировать: я решил эту проблему, создав свойство, которое загружает загруженного пользователя в HttpContext.Current.Items и проверяет его перед загрузкой в ​​соответствии с этой статьей.

Вы заявляете следующее:

Логично, что проблема, безусловно, должна заключаться в одной из следующих двух вещей:

  1. Кэш ISession не работает должным образом
  2. Каждый раз, когда пользователь запрашивается, он загружается с помощью
    другая сессия

Я думаю, что вы, возможно, путаете кэш первого (сессионного) уровня Nhibernate с кэшем второго уровня.

Сессии дешевы в изготовлении и выбрасывают. В веб-приложении вы обычно используете один сеанс на запрос. Как только вы получаете или загружаете объект в первый раз, он помещается в кэш первого уровня, который ограничен временем жизни сеанса. Когда сеанс закрывается и удаляется в конце запроса, у вас больше не будет доступа к объектам в кэше уровня сеанса. Действительно, каждый пользователь загружается разными сессиями - это совершенно нормально.

Кэш второго уровня ограничен временем жизни фабрики сеансов. Если кэш 2-го уровня включен, то после загрузки объекта по его первичному ключу он сохраняется в кэш-памяти 2-го уровня и может быть доступен для ВСЕХ сеансов без повторного обращения к базе данных, пока он не будет удален из кеша. Вам нужно будет явно включить кэширование для каждого объекта. Это поведение, которое вы ищете.

Дальнейшее чтение:

редактировать

Вам нужно будет выбрать Cache Provider из проекта NHContrib. Возможно, вам нужен SysCache2, который использует кэш Asp.Net, но вы можете использовать MemCached или Velocity или пару других, если хотите. Я также рекомендую вам попробовать Nhibernate Profiler. Я нашел это неоценимым в том, чтобы соваться под капотом и узнавать, чем занимается Nhibernate.

Вы можете использовать natural-id (NaturalId в Fluent NH) в своем отображении, чтобы обойти истечение срока действия кэша второго уровня только для этой ситуации.

Как вы заметили в своем вопросе, вы используете образ жизни PerWebRequest для хранилища, поэтому ISession (используемый хранилищем) будет воссоздан при каждом запросе. Я считаю, что это правильное поведение, ISession должен создаваться при каждом запросе, и каждая операция с NH должна выполняться.

Если вы хотите использовать одну ISession как синглтон, вы должны объявить образ жизни репозиториев как синглтон.

Я думаю, что в вашем приложении должен быть какой-то SessionProvider или SessionFactory, возможно, вы могли бы поработать над этим для одноэтапного сеанса.

НТН

Другие вопросы по тегам