NHibernate + WCF + Служба Windows и класс WcfOperationSessionContext
- У меня есть приложение службы Windows, в котором я создаю службы WCF в нем.
- Одним из сервисов являются сервисы данных: добавление, удаление, чтение, обновление данных через WCF.
- WCF использует NHibernate для манипулирования данными
Итак, мои предположения:
Какой-нибудь совет (лучшая практика) для управления сессиями для использования Hibernate с WCF?
Кто-нибудь знает что-нибудь о
WcfOperationSessionContext (hibernate 3.0) класс?
how to use it with WCF?
Хорошо, чтобы сделать это конкретным:
Предположим, что у меня есть служба WCF под названием DataServices
class WCFDataService .....
{
void SaveMyEntity(MyEntity entity)
{
.....................?? // How to do? Best Way
// Should i take one session and use it all times
// Should i take session and dipsose when operation finished then get
//new session for new operations?
// If many clients call my WCF service function at the same time?
// what may go wrong?
// etc....
}
}
И мне нужен класс NHibernateServiceProvider
class NHibernateServiceProvider ....
{
// How to get Session ?? Best way
ISession GetCurrentSession(){.... }
DisposeSession(){ ....}
}
С наилучшими пожеланиями
PS: я читал похожие записи здесь и другие веб-страницы. Но не вижу "конкретных" ответов.
4 ответа
Вот пост, описывающий подробно все шаги для регистрации и использования WcfOperationSessionContext. В него также включены инструкции по его использованию с проектом agatha-rrsl.
WcfOperationSessionContext, аналогичный ThreadStaticSessionContext и WebRequestSessionContext, является реализацией для контекста сеанса. Контекст сеанса используется для привязки (связывания) экземпляра ISession к определенному контексту.
Сеанс в текущем контексте можно получить, вызвав функцию ISessionFactory.GetCurrentSession().
Вы можете найти больше информации о контексте сеанса здесь.
WcfOperationSessionContext представляет контекст, который охватывает всю продолжительность операции WCF. Вам по-прежнему необходимо обрабатывать привязку сеанса в начале операции и отмену привязки / фиксации / удаления сеанса в конце операции.
Чтобы получить доступ к действиям начала / конца в конвейере wcf, вам нужно реализовать IDispatchMessageInspector. Вы можете увидеть образец здесь.
Также в отношении интеграции WCF: если вы используете контекст сеанса ThreadStatic, он будет работать в процессе разработки, но вы попадете в стену в производственном процессе, когда различные компоненты (например, авторизация, аутентификация) из конвейера wcf выполняются в разных потоках.
Что касается передового опыта, вы чуть не прибили его: используйте WcfOperationSessionContext для хранения текущего сеанса и IDispatchMessageInspector для начала / завершения вашей единицы работы.
РЕДАКТИРОВАТЬ - чтобы обратиться к деталям, которые вы добавили: Если вы настроили WcfOperationSessionContext и выполнили привязку / отмену привязки, как я объяснил выше, все, что вам нужно сделать, это внедрить ISessionFactory в вашу службу и просто использовать factory.GetCurrentSession(). Я выложу образец prj, если позволит время.
Вот пример проекта
Модель, которую мы используем для управления сессиями NHibernate с WCF, выглядит следующим образом:
1) У нас есть собственный класс ServiceHost, который наследуется от System.ServiceModel.ServiceHost, который также реализует ICallContextInitializer. Мы добавляем экземпляр хоста сервиса к каждой из операций в нашем сервисе следующим образом:
protected override void InitializeRuntime()
{
base.InitializeRuntime();
foreach (ChannelDispatcher cd in this.ChannelDispatchers)
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
foreach (DispatchOperation op in ed.DispatchRuntime.Operations)
{
op.CallContextInitializers.Add(this);
}
}
}
}
public void AfterInvoke(object correlationState)
{
// We don't do anything after the invoke
}
public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
OperationContext.Current.Extensions.Add(new SessionOperationContext());
return null;
}
BeforeInvoke просто гарантирует, что OperationContext для каждого вызова WCF имеет свой собственный сеанс. Мы обнаружили проблемы с IDispatchMessageInspector, когда сеанс недоступен во время сериализации ответов - проблема, если вы используете отложенную загрузку.
2) Затем вызывается наш SessionOperationContext, чтобы присоединиться, и мы используем событие OperationCompleted, чтобы удалить себя. Таким образом, мы можем быть уверены, что сессия будет доступна для сериализации ответов.
public class SessionOperationContext : IExtension<OperationContext>
{
public ISession Session { get; private set; }
public static SessionOperationContext Current
{
get
{
OperationContext oc = OperationContext.Current;
if (oc == null) throw new InvalidOperationException("Must be in an operation context.");
return oc.Extensions.Find<SessionOperationContext>();
}
}
public void Attach(OperationContext owner)
{
// Create the session and do anything else you required
this.Session = ... // Whatever instantiation method you use
// Hook into the OperationCompleted event which will be raised
// after the operation has completed and the response serialised.
owner.OperationCompleted += new EventHandler(OperationCompleted);
}
void OperationCompleted(object sender, EventArgs e)
{
// Tell WCF this extension is done
((OperationContext)sender).Extensions.Remove(this);
}
public void Detach(OperationContext owner)
{
// Close our session, do any cleanup, even auto commit
// transactions if required.
this.Session.Dispose();
this.Session = null;
}
}
Мы успешно использовали вышеуказанный шаблон в приложениях с высокой нагрузкой, и, похоже, он хорошо работает.
В целом, это похоже на то, что делает новый WcfOperationSessionContext (этого не было, когда мы разобрались с шаблоном выше;-)), но также преодолевает проблемы, связанные с отложенной загрузкой.
Что касается дополнительных вопросов: если вы используете модель, изложенную выше, вы просто сделаете следующее:
void SaveMyEntity(MyEntity entity)
{
SessionOperationContext.Current.Session.Save(entity);
}
Вам гарантируется, что сеанс всегда существует и что он будет удален после завершения операции WCF. При необходимости вы можете использовать транзакции обычным способом.
Хорошо, после нескольких дней чтения интернет-сообщений и т. Д. Все подходы, показанные в интернете, кажутся неправильными. Когда мы используем паттерн UnitOfWork с NH 3^ с транзакцией nhibernate, все апрохи вызывают исключения. Чтобы проверить это и доказать, что нам нужно создать тестовую среду с очередью транзакций MSMQ, специальный интерфейс с контрактом операции OneWay с установленной на нем транзакцией требуется. Этот подход должен работать так: 1. Мы помещаем транзакционное сообщение в очередь. 2. Сервис получает транзакционное сообщение из очереди. 3. Все работает, очередь пуста.
В некоторых случаях это не так уж сложно с интернет-подходами, это не работает должным образом. Итак, вот примеры, которые мы протестировали, которые не правы и почему:
- Подход Fabio Maulo: Использовать ICallContextInitializer - открыть сеанс / транзакцию NH на BeforeCall, после этого WCF выполняет метод обслуживания, на AfterCall в инициализаторе контекста мы вызываем session.Flush + Transactions.commit. Автоматически сеанс будет сохранен, когда область транзакции совершит операцию. В случае, когда при вызове транзакции. Полное исключение будет сгенерировано, WCF-служба отключится! Вопрос может быть в порядке, так что возьмите транзакцию. Завершите в разделе "попробуй / поймай" - отлично! - Без ошибок! Тогда область транзакции совершит транзакцию и сообщение будет взято из очереди, но данные не будут сохранены!
- Другой подход заключается в использовании IDispatchMessageInspector - вчера я подумал, что это лучший подход. Здесь нам нужно открыть сеанс / транзакцию в методе AfterReceiveRequest, после того, как WCF вызовет операцию службы для инспектора диспетчера сообщений BeforeSendReply. В этом методе у нас есть информация о [reply], которая в операции OneWay имеет значение null, но заполняется информацией об ошибках, если это произошло при вызове метода service. Здорово я подумала - вот это! но нет! Проблема в том, что на данный момент в конвейере обработки WCF у нас нет транзакции! Так что если вы выбросите транзакцию error.Complete throw или session.Flush, у нас не будет данных, сохраненных в базе данных, и сообщение не вернется в очередь, что не так.
Каково решение?
IOperationInvoker и только это!
Вам нужно реализовать этот интерфейс как шаблон декоратора по умолчанию в invoker. В методе Invoke перед вызовом мы открываем сессию / транзакцию открытой, затем мы вызываем invoke по умолчанию invoker, а после этого вызова action.complete в разделе finally мы вызываем session.flush. Какие типы проблем это решает: 1. У нас есть область транзакции на этом уровне, поэтому, когда сообщение об исключении завершенных бросков вернется в очередь, а WCF не завершит работу. 2. Когда вызов вызовет исключение, не будет вызвана транзакция. Не будет изменено состояние базы данных.
Я надеюсь, что это очистит всякую недостоверную информацию.
В свободное время я постараюсь написать пример.