NHibernate загружает один и тот же объект несколько раз - пожалуйста, помогите! - PullRequest
0 голосов
/ 07 января 2011

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

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

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

Я предполагаю, что проблема № 2),Я использую Castle Windsor для управления жизненными циклами объектов, поэтому я разместил часть кода, который использую на тот случай, если кто-то может помочь выявить проблему.Классы, управляемые 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 ]

2 голосов
/ 07 января 2011

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

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

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

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

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

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

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

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

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

1 голос
/ 07 января 2011

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

1 голос
/ 07 января 2011

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

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

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

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

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

1 голос
/ 07 января 2011

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

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

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

HTH

...