2011-01-07 7 views
0

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

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

  1. Кэш ISession «s не работает должным образом
  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 образе жизни.

Замок Виндзор контейнер просто статическое свойство своего рода служебный класс называется 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

Есть ли у вас какие-либо изменения, наследующие ваш класс от другого объекта? Опубликуйте свой класс сопоставления и сущности. – jishi

+0

В зависимости от параллелизма в вашем приложении вы должны позвонить о кешировании или нет. – Baz1nga

+0

Запустите NhProf и проверьте, не создано ли несколько сеансов для каждого запроса - у него есть 30-дневная бесплатная пробная версия. – DanB

ответ

1

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

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

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

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

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

+0

Вы можете сохранить пользователей PK вместо имени пользователя в качестве «имени пользователя» в билете FormsAuth. – DanB

+0

Спасибо за отличный совет, но в этом случае ничего хорошего. Обычно я использую аутентификацию NTLM для приложения (таким образом, имя пользователя - это имя пользователя Windows для входа в систему), но используйте базу данных для хранения дополнительной информации о каждом пользователе. В таблице в базе данных используется суррогатный ключ. Я знаю, что могу создавать дополнительные свойства в AD, но не по нескольким причинам. – David

1

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

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

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

НТН

+0

Очень важно использовать PerWebRequest с ISession, иначе разные сеансы пользователей NHibernate будут замешаны! Каждый раз, когда сеанс размывается, вы не представляете, сколько данных пользователей было сброшено или что они были в середине дела. – David

+0

Вы действительно не хотите делать сеанс в одноэлементном. – DanB

2

Вы утверждаете следующее:

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

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

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

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

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

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

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

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

+0

Я определенно думаю о кеше первого уровня - кэш-память на ISession (веб-запрос). Я никогда не думал о настройке кеша второго уровня, но я полагаю, что это может быть полезно для хранения пользовательских данных между запросами. (Я немного обеспокоен тем, что данные затмевают, хотя.) Спасибо за мысль! – David

+0

Удивительный ответ .. +1 для всех ссылок .. – Baz1nga

+0

@ Давид: А, ок. У вас возникают проблемы, когда разные сеансы используются в одном и том же веб-запросе? – DanB

1

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

+0

Я не использую кеш второго уровня, но в противном случае это было бы фантастическим предложением. Почему natural-id делает объекты кэшируемыми только в кэше второго уровня ??? – David