Управление сессией RavenDb в Виндзоре под NServiceBus

Я использую NServiceBus (3.2.2), RavenDB (1.2.2017-Unstable) и Windsor (3.0.0.4001) в проекте MVC 4.

У меня есть класс IHandleMessages, который обрабатывает 3 разных сообщения, и для которого требуется IDocumentSession, и, следовательно, определяет такое свойство, как:

public IDocumentSession DocumentSession { get; set; }

Я скопировал реализацию RavenDbUnitOfWork с веб- сайта NServiceBus

Я зарегистрировал IDocumentStore, IDocumentSession и IManageUnitsOfWork в своем контейнере Windsor следующим образом:

container.Register(
            Component
                .For<IManageUnitsOfWork>()
                .ImplementedBy<RavenUnitOfWork>()
                .LifestyleTransient()
            );
container.Register(
            Component
                .For<IDocumentStore>()
                .UsingFactoryMethod(k => DocumentStoreHolder.DocumentStore)
                .LifestyleSingleton(),
            Component
                .For<IDocumentSession>()
                .UsingFactoryMethod(k => k.Resolve<IDocumentStore>().OpenSession())
                .LifestyleTransient()
            );

NServiceBus настроен на использование моего контейнера:

Configure.With()
         .CastleWindsorBuilder(container);

Я сталкиваюсь с проблемой, что UnitOfWork и обработчик сообщений получают разные экземпляры DocumentSession. Это означает, что объекты, хранящиеся в сеансе в обработчике сообщений, не сохраняются, так как SaveChanges() вызывается в другом DocumentSession.

Удаление временного образа жизни вызывает проблемы различного рода, которые приводят к параллелизму / конфликтам при обновлении объектов из RavenDb, поскольку (вероятно) обработчик сообщений продолжает получать один и тот же экземпляр DocumentSession, который содержит кэшированную версию обновленного объекта.

Обновить:

Как и предполагалось, я попытался изменить регистрацию IDocumentSession в Виндзоре на стиль жизни Scope, например:

Component
    .For<IDocumentSession>()
    .UsingFactoryMethod(k => k.Resolve<IDocumentStore>().OpenSession())
    .LifestyleScope()

Это вызывает исключения, когда контейнер пытается разрешить контроллер MVC, говоря, что область не найдена, и спрашивая, не забыл ли я вызвать BeginScope().

3 ответа

У вас должна быть область действия на сообщение, а не временная или единичная.

Я предполагаю, что ваш контроллер MVC имеет прямую зависимость от IDocumentStore. Вам необходимо вызывать container.BeginScope() перед каждым запросом из Интернета. Вы можете сделать это как атрибут фильтра действий http://msdn.microsoft.com/en-us/library/system.web.mvc.actionfilterattribute.aspx или как аспект AOP на самом контроллере http://cangencer.wordpress.com/2011/06/02/asp-net-mvc-3-aspect-oriented-programming-with-castle-interceptors/.

Проблема в том, что вам нужен другой образ жизни при использовании nservicebus на веб-сайте asp.net mvc при совместном использовании IDocumentSession в том же контейнере.

Для ASP.NET MVC вам нужен стиль жизни PerWebRequest, а для NServiceBus вам нужен стиль жизни Scoped.

Чтобы сделать это, я использовал гибридный код образа жизни в проекте содействия замку: https://github.com/castleprojectcontrib/Castle.Windsor.Lifestyles/tree/master/Castle.Windsor.Lifestyles

При вызове из контекста ASP.NET он использует WebRequestScopeAccessor. Для NServicebus вам нужен LifetimeScopeAccessor. Этого нет в проекте contrib, но его легко добавить:

public class HybridPerWebRequestLifetimeScopeScopeAccessor : HybridPerWebRequestScopeAccessor
{
    public HybridPerWebRequestLifetimeScopeScopeAccessor()
        : base(new LifetimeScopeAccessor())
    {
    }
}

И в вашем регистрационном коде вам нужно что-то вроде:

container.Register(Component.For<IDocumentSession>().LifestyleScoped<HybridPerWebRequestLifetimeScopeScopeAccessor>().UsingFactoryMethod(() => RavenDbManager.DocumentStore.OpenSession()));

А вот реализация Rhino Service Bus, которую я использовал перед переключением на nservicebus:

https://gist.github.com/4655544

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