在NServiceBus下管理Windsor中的RavenDb会话

时间:2012-08-02 09:11:44

标签: castle-windsor nservicebus ravendb

我在MVC 4项目中使用NServiceBus(3.2.2),RavenDB(1.2.2017-Unstable)和Windsor(3.0.0.4001)。

我有一个处理3个不同消息的IHandleMessages类,它需要一个IDocumentSession,因此定义了一个属性,如:

public IDocumentSession DocumentSession { get; set; }

我从NServiceBus'website

复制了RavenDbUnitOfWork实现

我在我的Windsor容器中注册了IDocumentStore,IDocumentSession和IManageUnitsOfWork,如下所示:

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的不同实例。这意味着不会保存存储在消息处理程序中的会话中的对象,因为在另一个DocumentSession上调用了SaveChanges()。

删除Transient生活方式会导致不同类型的问题,从RavenDb更新对象时会导致并发/冲突,因为(可能)消息处理程序不断获取DocumentSession的相同实例,该实例包含更新对象的缓存版本

更新

正如所建议的那样,我已经尝试将Windsor中IDocumentSession的注册更改为Scope生活方式,如下所示:

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

当容器尝试解析MVC控制器时,这会导致异常,说找不到范围,并询问我是否忘记调用BeginScope()。

3 个答案:

答案 0 :(得分:4)

您需要具有每条消息的范围,而不是瞬态或单例。

答案 1 :(得分:0)

我假设你的mvc控制器直接依赖于IDocumentStore。您需要在来自Web的每个请求之前调用container.BeginScope()。您可以将此操作作为操作过滤器属性http://msdn.microsoft.com/en-us/library/system.web.mvc.actionfilterattribute.aspx或作为控制器本身的{A}方面http://cangencer.wordpress.com/2011/06/02/asp-net-mvc-3-aspect-oriented-programming-with-castle-interceptors/

答案 2 :(得分:0)

问题是,当您在同一容器中共享IDocumentSession时,在asp.net mvc网站上使用nservicebus时,您需要不同的生活方式。

对于ASP.NET MVC,您需要PerWebRequest生活方式,对于NServiceBus,您需要Scoped生活方式。

为此,我在castle contrib项目中使用了混合生活方式代码: 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()));

这是我在切换到nservicebus之前使用的Rhino Service Bus的实现:

https://gist.github.com/4655544