Очень простое консольное приложение с CastleWindsor, Rebus, RabbitMQ.Почему «Сервис IBus уже зарегистрирован в этом контейнере»? - PullRequest
0 голосов
/ 24 ноября 2018

Только для тестирования и дидактического дирекции, не имеющего строгой привязки к этому вопросу, я писал пятиминутное консольное приложение для отправки события в RabbitMQ с использованием Rebus.Моя цель - только опубликовать событие, но у меня есть некоторые проблемы при регистрации шины с помощью WindsorCastle.На моей работе у нас есть несколько Windows Service Jobs, которые делают это изящным образом, сейчас дома возникают проблемы с чем-то, что кажется мне простым, но я думаю, что мне не хватает некоторых основ IOC, может быть ...

ЭтоProgram.cs, в котором я использую попросить WindsorCastle установить контейнер для IOC:

using Castle.Windsor;
using Serilog;
using System;

namespace RebusRabbitEmitEvent
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .WriteTo.ColoredConsole(outputTemplate: "{Timestamp:HH:mm:ss} {Message}{NewLine}{Exception}")
                .CreateLogger();

            using (var container = new WindsorContainer())
            {

                container
                    .Install(new Installer());

                EventEmitter emitter = container.Resolve<EventEmitter>();

                emitter.Emit();
                Console.WriteLine("Press ENTER to quit");
                Console.ReadLine();
            }
        }
    }
}

Вот Installer.cs, в котором я установил все DI:

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using Rebus.Activation;
using Rebus.Bus;
using Rebus.Config;
using Rebus.Retry.Simple;

namespace RebusRabbitEmitEvent
{
    public class Installer : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(

                Component.For<IBus>()
                    .UsingFactoryMethod(k =>
                    {
                        var cnstring = "amqp://guest:guest@localhost:5672";
                        var inputQueueName = "Pippo.Input";
                        var errorQueueName = "Pippo.Error";
                        return Configure.With(k.Resolve<IHandlerActivator>())
                            .Logging(l => l.ColoredConsole())
                            .Transport(t => t.UseRabbitMq(cnstring, inputQueueName))
                            .Routing(r => r.TypeBasedRoutingFromAppConfig())
                            .Options(o => o.SimpleRetryStrategy(errorQueueAddress: errorQueueName, maxDeliveryAttempts: 2))
                            .Options(o => o.SetNumberOfWorkers(1))
                            .Options(o => o.SetMaxParallelism(1))
                            .Start();
                    }).LifestyleSingleton(),

                Component.For<IHandlerActivator>()
                    .UsingFactoryMethod(k => new CastleWindsorContainerAdapter(container)),

                Component.For<EventEmitter>()
                    .UsingFactoryMethod(k =>
                    {
                        return new EventEmitter(k.Resolve<IBus>());
                    })
            );
        }
    }
}

Вот EventEmitter.cs, в котором я публикую событие:

using Rebus.Bus;
using RebusRabbitEmitEvent.Events;
using System;

public class EventEmitter
{
    private IBus _bus = null;

    public EventEmitter(IBus bus)
    {
        _bus = bus;
    }

    public void Emit()
    {
        _bus.Publish(new OperationDoneEvent(Guid.NewGuid(), 0, "Valore del parametro")).Wait();
    }
}

Здесь код события:

using System;

namespace RebusRabbitEmitEvent.Events
{
    public interface IEvent
    {
        Guid Id { get; }
    }


    [Serializable]
    public class Event : IEvent
    {
        public Guid Id { get; private set; }
        public int Version;

        public Event(Guid id, int version)
        {
            this.Id = id;
            this.Version = version;
        }
    }

    public class OperationDoneEvent : Event
    {
        public string ParameterPassedThroughEvent { get; protected set; }

        public OperationDoneEvent(Guid id, int version, string parameterPassedThroughEvent) : base(id, version)
        {
            ParameterPassedThroughEvent = parameterPassedThroughEvent;
        }
    }

}

Когда я запускаю программу, я получаю исключение на Installer.cs во время возвратаConfigure.With (k.Resolve ()) , исключение говорит: « System.InvalidOperationException:« Служба IBus уже зарегистрирована в этом контейнере. Если вы хотите разместить несколько экземпляров Rebus в одном процессе,используйте для них отдельные экземпляры контейнеров. ' "

Любой другой объект инъекции зависимостей, который я тестирую, отлично работает.+ Если я не использую WindsorCastle и не устанавливаю шину непосредственно в EventEmitter.cs, программа работает хорошо.

Есть ли что-то не так?Что я неправильно понимаю?

ОБНОВЛЕНИЕ:

Я скачал исходный код Rebus.CastleWindsor.Я вижу, что с моим кодом при нажатии CastleWindsorContainerAdapter у windsorContainer уже есть IBus:

enter image description here

Я попытался немного изменить установщик, удалить заводской методIBus и создания IBus непосредственно в заводском методе EventEmitter, например:

 Component.For<EventEmitter>()
                    .UsingFactoryMethod(k =>
                    {
                        var cnstring = "amqp://guest:guest@localhost:5672";
                        var inputQueueName = "Pippo.Input";
                        var errorQueueName = "Pippo.Error";

                        IBus bus = Configure.With(new CastleWindsorContainerAdapter(container))
                            .Logging(l => l.ColoredConsole())
                            .Transport(t => t.UseRabbitMq(cnstring, inputQueueName))
                            .Routing(r => r.TypeBasedRoutingFromAppConfig())
                            .Options(o => o.SimpleRetryStrategy(errorQueueAddress: errorQueueName, maxDeliveryAttempts: 2))
                            .Options(o => o.SetNumberOfWorkers(1))
                            .Options(o => o.SetMaxParallelism(1))
                            .Start();

                        return new EventEmitter(bus);
                    })

Теперь в ctor CastleWindsorContainerAdapter, в windsorContainer больше нет IBus:

enter image description here

С этим изменением метод SetBus CastleWindsorContainerAdapter не выдает исключение, и код работает.

А этот момент я немного запутался, я думал, что оригинал был вправильно, я догадался, что в «вернуть новый EventEmitter (k.Resolve ());»в решении использовался бы заводской метод IBus, я видел много Windows Service с аналогичным установщиком, повторяю, может быть, мне не хватает некоторых основ IOC, или, может быть, что-то особенное в этой ситуации ..?

ОБНОВЛЕНИЕ 2:

Как указано в mookid8000 в его комментарии к ответу, IBUS не должен повторно обрабатываться в контейнере, как, впрочем, и в его ответе (и примере в документации).Выкладываю полный правильный установщик для наглядности:

namespace RebusRabbitEmitEvent
{
    public class Installer : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            Configure.With(new CastleWindsorContainerAdapter(container))
                        .Logging(l => l.ColoredConsole())
                        .Transport(t => t.UseRabbitMq("amqp://guest:guest@localhost:5672", "Pippo.Input"))
                        .Routing(r => r.TypeBasedRoutingFromAppConfig())
                        .Options(o => o.SimpleRetryStrategy(errorQueueAddress: "Pippo.Error", maxDeliveryAttempts: 2))
                        .Options(o => o.SetNumberOfWorkers(1))
                        .Options(o => o.SetMaxParallelism(1))
                        .Start();

            container.Register(
                Component.For<EventEmitter>()
                    .UsingFactoryMethod(k =>
                    {
                        return new EventEmitter(k.Resolve<IBus>());
                    })
            );
        }
    }

1 Ответ

0 голосов
/ 25 ноября 2018

Ошибка просто в том, что вы не позволяете интеграции Rebus 'Castle Windsor регистрироваться в контейнере.

Правильный способ настройки Rebus с Windsor заключается в следующем:

Configure.With(new CastleWindsorContainerAdapter(container))
    .(...)
    .Start();

Например, от установщика, подобного этому:

public class RebusInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        Configure.With(new CastleWindsorContainerAdapter(container))
            .(...)
            .Start();
    }
}

Т.е.: не регистрируйте IBus (или ISyncBus или IMessageContext в этом отношении) в контейнере самостоятельно, потому что Ребус будетсделать это, когда вы используете один из его контейнерных адаптеров.

Изучите readme для более подробного объяснения.

...