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. Все работает, очередь пуста.

В некоторых случаях это не так уж сложно с интернет-подходами, это не работает должным образом. Итак, вот примеры, которые мы протестировали, которые не правы и почему:

  1. Подход Fabio Maulo: Использовать ICallContextInitializer - открыть сеанс / транзакцию NH на BeforeCall, после этого WCF выполняет метод обслуживания, на AfterCall в инициализаторе контекста мы вызываем session.Flush + Transactions.commit. Автоматически сеанс будет сохранен, когда область транзакции совершит операцию. В случае, когда при вызове транзакции. Полное исключение будет сгенерировано, WCF-служба отключится! Вопрос может быть в порядке, так что возьмите транзакцию. Завершите в разделе "попробуй / поймай" - отлично! - Без ошибок! Тогда область транзакции совершит транзакцию и сообщение будет взято из очереди, но данные не будут сохранены!
  2. Другой подход заключается в использовании 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. Когда вызов вызовет исключение, не будет вызвана транзакция. Не будет изменено состояние базы данных.

Я надеюсь, что это очистит всякую недостоверную информацию.

В свободное время я постараюсь написать пример.

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