Большинству друзей, использующих NHibernate для веб-разработки, известен режим Session-Per-Request, но в Интернете не так много примеров его правильного использования. Многие статьи в Интернете, в том числе и в саду, допустили одну и ту же ошибку. и эта ошибка распространяется...
Давайте сначала посмотрим на статью Flyear в саду «Простая реализация NHibernate One Session Per Request».
Нет ничего плохого в том, чтобы сначала настроить NHibernate:
<property name='current_session_context_class'>веб</property>
Ошибка находится в классе NHinbernateSessionFactory (все имена классов неверны).
общедоступный запечатанный класс NHibernateHelper
{
общественный статический только для чтения ISessionFactory SessionFactory;
staticNHibernateHelper()
{
SessionFactory = новая конфигурация()
.Настроить()
.AddAssembly(/**/)
.BuildSessionFactory();
}
общедоступный статический ISession GetCurrentSession()
{
вернуть SessionFactory.GetCurrentSession();
}
}
Настройте current_session_context_class как веб, и NHibernate сгенерирует экземпляр класса NHibernate.Context.WebSessionContext во время инициализации. Исходный код класса WebSessionContext выглядит следующим образом:
[Сериализуемый]
общедоступный класс WebSessionContext: MapBasedSessionContext
{
//Поля
частная константная строка SessionFactoryMapKey = "NHibernate.Context.WebSessionContext.SessionFactoryMapKey";
// Методы
public WebSessionContext (фабрика ISessionFactoryImplementor): база (фабрика)
{ }
защищенное переопределение IDictionary GetMap()
{
return (HttpContext.Current.Items[SessionFactoryMapKey] как IDictionary);
}
защищенное переопределение void SetMap (значение IDictionary)
{
HttpContext.Current.Items[SessionFactoryMapKey] = значение;
}
}
WebSessionContext реализует режим Session-Per-Request, который инкапсулирует HttpContext, поэтому нам не нужно использовать HttpContext в нашем вспомогательном классе (NHibernateSessionFactory или NHibernateHelper).
Нам просто нужно получить сеанс из экземпляра WebSessionContext. Получить текущий ISession из класса WebSessionContext довольно просто, поскольку WebSessionContext реализует интерфейс ICurrentSessionContext:
общедоступный интерфейс ICurrentSessionContext
{
ISession CurrentSession();
}
Классы и интерфейсы в пространстве имен NHibernate.Context
(Примечание: current_session_context_class также можно настроить как Managed_web, Call, thread_static, что соответствует классам ManagedWebSessionContext, CallSessionContext и ThreadStaticSessionContext соответственно)
При фактическом использовании нам не нужно напрямую вызывать метод CurrentSession() WebSessionContext, поскольку ISessionFactory предоставляет более простой метод, который позволяет нам получить сеанс за один шаг:
общедоступный интерфейс ISessionFactory: IDisposable
{
ISession GetCurrentSession();
//......
}
Давайте обсудим конкретную реализацию метода ISessionFactory.GetCurrentSession:
Метод Configuration.BuildSessionFactory фактически возвращает экземпляр класса SessionFactoryImpl. Давайте кратко рассмотрим часть кода SessionFactoryImpl:
1 общедоступный запечатанный класс SessionFactoryImpl
2: ISessionFactoryImplementor, IMapping, ISessionFactory, IDisposable, IObjectReference
3 {
4 частный только для чтения ICurrentSessionContext currentSessionContext;
5
6 общедоступный ISession GetCurrentSession()
7 {
8, если (this.currentSessionContext == null)
9 {
10 выдать новое исключение HibernateException(
11 "ТекущийSessionContext не настроен (установите свойство current_session_context_class)!");
12}
13 вернуть this.currentSessionContext.CurrentSession();
14}
15
16 общедоступный ICurrentSessionContext CurrentSessionContext
17 {
18 получить {вернуть this.currentSessionContext};
19}
20
21 частный ICurrentSessionContext BuildCurrentSessionContext()
двадцать два {
23 имя строки = PropertiesHelper.GetString("current_session_context_class", this.properties, null);
24 строка str2 = имя;
25, если (str2 != ноль)
26 {
27 if (str2 == "call") вернуть новый CallSessionContext(this);
28 if (str2 == "thread_static") вернуть новый ThreadStaticSessionContext(this);
29 if (str2 == "web") вернуть новый WebSessionContext(this);
30 if (str2 == "managed_web") вернуть новый ManagedWebSessionContext(this);
31}
еще 32
33 возвращает ноль;
34 попытки
35 {
36 Тип типа = ReflectHelper.ClassForName(name);
37 возврат (ICurrentSessionContext)Environment.BytecodeProvider.ObjectsFactory
38.CreateInstance(тип, новый объект[] {это});
39 }
40 уловов (исключение)
41 {
42 log.Error("Невозможно создать контекст текущего сеанса [" + имя + "]", исключение);
43 возвращает ноль;
44}
45 }
46 //......
47 }
При создании экземпляра SessionFactoryImpl он вызывает метод BuildCurrentSessionContext() (строка 21), чтобы присвоить значение полю currentSessionContext. Конкретное значение определяется current_session_context_class в файле конфигурации.
GetCurrentSession() ISessionFactory вызывает метод CurrentSession() ICurrentSessionContext (строка 6).
Приведенный выше код довольно прост, его уже должен понимать каждый.
В статье «Простая реализация NHibernate One Session Per Request» есть два неуместных места:
1. Не каждый запрос требует сеанса для доступа к базе данных. Код Global.asax в статье выполняет WebSessionContext.Bind() в начале всех запросов, что приведет к потере большого количества сеанса. Хотя сеанс NHibernate является легковесным, более разумным подходом является «действительно необходимая» привязка времени.
2. Поскольку для метода WebSessionContext.Unbind требуется экземпляр интерфейса ISessionFactory, мы вынуждены использовать наш вспомогательный класс (NHibernateSessionFactory или NHibernateHelper) для предоставления SessionFactory.
Первый вопрос решить проще, поэтому оставьте его в ответе, чтобы связаться со всеми.
На второй вопрос я отвечу в следующем эссе.
Я давно не изучаю NHibernate и являюсь новичком. Если есть ошибки, поправьте меня!