我在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()。
答案 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的实现: