5

我在 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 的网站复制了 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 的不同实例的问题。这意味着存储在消息处理程序中的会话中的对象不会被保存,因为 SaveChanges() 是在不同的 DocumentSession 上调用的。

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

更新:

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

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

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

4

3 回答 3

4

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

于 2012-08-02T18:44:56.237 回答
0

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

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

为此,我在城堡 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

于 2013-01-28T13:36:49.957 回答
0

我假设您的 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/

于 2012-08-15T06:15:27.903 回答