Asp.net mvc 基于内容的Rebus多队列

Asp.net mvc 基于内容的Rebus多队列,asp.net-mvc,simple-injector,azure-servicebus-queues,rebus,Asp.net Mvc,Simple Injector,Azure Servicebus Queues,Rebus,设置:使用SimpleInjector在asp.net mvc项目中重新创建 我需要创建两个处理程序来接收消息,每个处理程序都来自一个特定的队列。通过遵循我在上面的发现,我创建了类似的代码 在类库中,我有一个实现SimpleInjectorIPackage的类,其代码如下: public void RegisterServices( Container container ) { container.Register<IHandleMessages<MyMessage>

设置:使用SimpleInjector在asp.net mvc项目中重新创建

我需要创建两个处理程序来接收消息,每个处理程序都来自一个特定的队列。通过遵循我在上面的发现,我创建了类似的代码

在类库中,我有一个实现SimpleInjector
IPackage
的类,其代码如下:

public void RegisterServices( Container container ) {
    container.Register<IHandleMessages<MyMessage>, MyMessageHandler>( Lifestyle.Scoped );
    IContainerAdapter adapter = new SimpleInjectorContainerAdapter( container );
    Configure.With( adapter )
             .Transport( t => t.UseAzureServiceBus( connectionString, "A_QUEUE_NAME", AzureServiceBusMode.Standard ) )
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "A_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();

    Configure.With(adapter
             .Transport(t => t.UseAzureServiceBus(connectionString, "B_QUEUE_NAME")
             .Options( oc => {
                 oc.SetNumberOfWorkers( 1 );
                 oc.SimpleRetryStrategy( errorQueueAddress: "B_ERROR_QUEUE_NAME", maxDeliveryAttempts: 3 );
             } )
             .Start();
}
编辑

然后,我删除了第二个
Configure.With(…)
代码块,现在,当我执行
\u bus.Send(message)
时,我在消费者过程中遇到另一个错误,它说

处理ID为fef3acca-97f4-4495-b09d-96e6c9f66c4d的消息时出现未处理的异常1:SimpleInjector.ActivationException:找不到类型IEnumerable的注册。但是,IHandleMessages有一个注册;您是想调用GetInstance()还是依赖IHandleMessages?或者您的意思是使用RegisterCollection注册一个类型集合

堆栈跟踪:

2017-04-13 10:21:03,805 [77] WARN  Rebus.Retry.ErrorTracking.InMemErrorTracker - 
   at SimpleInjector.Container.ThrowMissingInstanceProducerException(Type serviceType)
   at SimpleInjector.Container.GetInstanceForRootType[TService]()
   at SimpleInjector.Container.GetInstance[TService]()
   at SimpleInjector.Container.GetAllInstances[TService]()
   at Rebus.SimpleInjector.SimpleInjectorContainerAdapter.<GetHandlers>d__3`1.MoveNext()
2017-04-13 10:21:03805[77]警告Rebus.Retry.ErrorTracking.InMemErrorTracker-
在SimpleInjector.Container.ThrowMissingInstanceProducerException(类型serviceType)
在SimpleInjector.Container.GetInstanceErrorOttoType[TService]()
在SimpleInjector.Container.GetInstance[TService]()
在SimpleInjector.Container.GetAllInstances[TService]()
在Rebus.SimpleInjector.SimpleInjectorContainerAdapter.d_u3`1.MoveNext()中

异常状态为:“类型IBus已注册”。根据堆栈跟踪,第二次添加IBU是在
SimpleInjectorContainerAdapter
内。你必须知道它是什么时候第一次注册的。这很容易做到;刚刚注册了一个虚拟的
IBus
作为创建
容器后的第一个注册,并查看它爆炸的堆栈跟踪。

我通常建议每个容器实例只保留一个
IBus
,因为总线本身可以被视为“应用程序”,这恰好与IoC容器是一个可以在应用程序生命周期内“承载”应用程序的对象这一事实非常吻合

Rebus没有提供一致的容器抽象,因为我同意Mark Seemann的观点,那是一个注定要失败的项目。事实上,正如Rebus所使用的那样,Rebus用于提供处理程序的自动注册,但结果证明这是有问题的

相反,Rebus鼓励您提供一个“容器适配器”(IContainerAdapter的实现),其职责是:

  • 查找处理程序
  • 提供以正确方式注册
    IBU
    IMessageContext
    的方法
如果为Autofac、Castle Windsor、SimpleInjector等提供了现成的容器适配器,则不需要提供容器适配器–Configure.With(…)
rant只接受“handler activator”(实现
IHandlerActivator
),因此,如果您只想使用IoC容器查找处理程序并自行注册
IBU
,也可以通过实现
IHandlerActivator
并在容器中查找处理程序来实现

TL;DR:Rebus方法是将IoC容器的一个实例视为一个单独的应用程序,因此在其中只注册一个
IBU
是有意义的

如果您希望在一个进程中承载多个应用程序(甚至是具有不同消息SLA的应用程序的多个实例),可以创建多个容器实例。您引用了我的SO答案(问题),因此我将与您分享我是如何实现它的。 正如您将看到的,使用特定的接口,我将命令与事件分开

然后,就在队列的消费部分,我进行了这种注册:

    public static void Register()
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies()
            .Where(i => i.FullName.StartsWith("MySolutionPrefix"))
            ;

        var container = new Container();

        // http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perexecutioncontextscope
        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();


        var serviceType = typeof(IHandleMessages<>).Name;
        // this is extension method to get specific handlers
        IEnumerable<Type> commandHandlers = assemblies.GetHandlers(serviceType, typeof(ICommand));
        container.Register(typeof(IHandleMessages<>), commandHandlers.Concat(new List<Type> { typeof(HistorizeCommandHanlder) }));

        // NOTE Just command Handlers
        container.RegisterCollection(typeof(IHandleMessages<>), commandHandlers);


        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            //.... logging, transport (I created my own transport on mongoDb), routing, sagas and so on
            .Options(o =>
            {
                // This simply my personal transport as Register<IOneWayClientTransport> 
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                // this is more complicated because i want that automatically the message is copied on
                // a separate topic for each handler
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        // It is necessary because otherwise it sends published messages to no-one 
        commandHandlers.GetHandledSubTypes(serviceType, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Subscribe(i).Wait());

        // NOTE just events handlers
        IEnumerable<Type> eventHandlers = assemblies
            .GetHandlers(serviceType, typeof(IVersionedEvent))
            .Except(commandHandlers)
            //.Except(new List<Type> { typeof(TempHandlerLogDecorator) })
            ;
        foreach (var handler in eventHandlers)
            ConfigureServiceBus(mongoDbConnectionProvider, db, handler.FullName, new[] { handler });
    }

    private static IBus ConfigureServiceBus(MongoDbConnectionProvider mongoDbConnectionProvider, IMongoDatabase db, string inputQueueName, IEnumerable<Type> handlers)
    {
        var container = new Container();

        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();

        container.RegisterCollection(typeof(IHandleMessages<>), handlers);

        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            .... logging, Subscriptions
            // this is a consumer only for inputQueueName
            .Transport(t => t.UseMongoDb(db, settings.TopicBasedMessageQueueName, inputQueueName))
            .Options(o =>
            {
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        handlers.GetHandledSubTypes(typeof(IHandleMessages<>).Name, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Advanced.Topics.Subscribe(i.GetDecoupledTopic(settings.DecouplingHandlersRegistration)).Wait());

        return bus;
    }
公共静态无效寄存器()
{
var assemblies=AppDomain.CurrentDomain.GetAssemblys()
.Where(i=>i.FullName.StartsWith(“MySolutionPrefix”))
;
var container=新容器();
// http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perexecutioncontextscope
container.Options.DefaultScopedLifestyle=新的ExecutionContextScopeLifestyle();
var serviceType=typeof(IHandleMessages).Name;
//这是获取特定处理程序的扩展方法
IEnumerable commandHandlers=assemblies.GetHandlers(serviceType,typeof(ICommand));
Register(typeof(IHandleMessages),commandHandlers.Concat(新列表{typeof(historizeCommandHandler)});
//请注意,只有命令处理程序
RegisterCollection(typeof(IHandleMessages),commandHandler);
var bus=Configure.With(新的SimpleInjectorContainerAdapter(容器))
//…日志记录、传输(我在mongoDb上创建了自己的传输)、路由、sagas等等
.选项(o=>
{
//这只是我的个人交通登记
o、 ConfigureDecouplingDatabase(db,settings.TopicBasedMessageQueueName);
//这是更复杂的,因为我想自动复制的消息
//每个处理程序都有一个单独的主题
o、 启用HandlerDecoupling(设置.DecouplingHandlersRegistration);
})
.Start();
container.Verify();
//这是必要的,因为否则它不会向任何人发送已发布的消息
GetHandledSubTypes(serviceType,typeof(IVersionedEvent))
托利斯先生()
.ForEach(i=>bus.Subscribe(i.Wait());
//只需注意事件处理程序
IEnumerable eventHandlers=程序集
.GetHandlers(serviceType,typeof(IVersionedEvent))
。除(命令处理程序)
//.Exception(新列表{typeof(TempHandlerLogDecorator)})
    public static void Register()
    {
        var assemblies = AppDomain.CurrentDomain.GetAssemblies()
            .Where(i => i.FullName.StartsWith("MySolutionPrefix"))
            ;

        var container = new Container();

        // http://simpleinjector.readthedocs.io/en/latest/lifetimes.html#perexecutioncontextscope
        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();


        var serviceType = typeof(IHandleMessages<>).Name;
        // this is extension method to get specific handlers
        IEnumerable<Type> commandHandlers = assemblies.GetHandlers(serviceType, typeof(ICommand));
        container.Register(typeof(IHandleMessages<>), commandHandlers.Concat(new List<Type> { typeof(HistorizeCommandHanlder) }));

        // NOTE Just command Handlers
        container.RegisterCollection(typeof(IHandleMessages<>), commandHandlers);


        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            //.... logging, transport (I created my own transport on mongoDb), routing, sagas and so on
            .Options(o =>
            {
                // This simply my personal transport as Register<IOneWayClientTransport> 
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                // this is more complicated because i want that automatically the message is copied on
                // a separate topic for each handler
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        // It is necessary because otherwise it sends published messages to no-one 
        commandHandlers.GetHandledSubTypes(serviceType, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Subscribe(i).Wait());

        // NOTE just events handlers
        IEnumerable<Type> eventHandlers = assemblies
            .GetHandlers(serviceType, typeof(IVersionedEvent))
            .Except(commandHandlers)
            //.Except(new List<Type> { typeof(TempHandlerLogDecorator) })
            ;
        foreach (var handler in eventHandlers)
            ConfigureServiceBus(mongoDbConnectionProvider, db, handler.FullName, new[] { handler });
    }

    private static IBus ConfigureServiceBus(MongoDbConnectionProvider mongoDbConnectionProvider, IMongoDatabase db, string inputQueueName, IEnumerable<Type> handlers)
    {
        var container = new Container();

        container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();

        container.RegisterCollection(typeof(IHandleMessages<>), handlers);

        var bus = Configure.With(new SimpleInjectorContainerAdapter(container))
            .... logging, Subscriptions
            // this is a consumer only for inputQueueName
            .Transport(t => t.UseMongoDb(db, settings.TopicBasedMessageQueueName, inputQueueName))
            .Options(o =>
            {
                o.ConfigureDecouplingDatabase(db, settings.TopicBasedMessageQueueName);
                o.EnableHandlerDecoupling(settings.DecouplingHandlersRegistration);
            })
            .Start();

        container.Verify();

        handlers.GetHandledSubTypes(typeof(IHandleMessages<>).Name, typeof(IVersionedEvent))
            .ToList()
            .ForEach(i => bus.Advanced.Topics.Subscribe(i.GetDecoupledTopic(settings.DecouplingHandlersRegistration)).Wait());

        return bus;
    }