Установка свойства Name при регистрации компонента с использованием контейнера IoC Castle Windsor - PullRequest
4 голосов
/ 20 января 2012

В моем приложении у меня есть класс с именем Message.В классе Message существует свойство с именем MessageType типа string.Свойство MessageType используется для предупреждения приложения о том, какая схема данных будет существовать в экземпляре класса Message.Класс Message является производным от интерфейса IMessage.

В качестве примера, скажем, у нас есть экземпляр класса Message, у которого свойство MessageType имеет значение "com.business.product.RegisterUser".

Каждая схема MessageType имеет соответствующий класс обработчика сообщений (MessageHandler), который является производным от интерфейса IMessageHandler.В случае вышеупомянутого примера, будет класс с именем RegisterUserMessageHandler.Все классы обработчиков сообщений являются производными от IMessageHandler.IMessageHandler определяет функцию GetMessageType, которая будет возвращать свойство MessageType экземпляра производного сообщения.

При регистрации классов / компонентов с помощью контейнера IoC Castle Windsor я хотел бы установить для свойства Name значение MessageType экземпляра Message.имущество.Я также хотел бы зарегистрировать эти классы / компоненты, используя для этого метод «зарегистрировать компоненты по соглашениям».

Конечная цель этого упражнения - разрешить моему коду вызывать метод Resolve с использованием свойства MessageTypeСообщение для получения правильного экземпляра обработчика сообщений.Например:

string messageType = "com.business.product.RegisterUser";

IMessageHandler registerUserMessageHandler = _container.Resolve<IMessageHandler>(messageType);

Буду признателен за любую помощь, чтобы решить эту проблему.Большое спасибо.

Ответы [ 2 ]

3 голосов
/ 21 января 2012

Вот решение, которое я придумал, но у него есть некоторые недостатки. Вам придется решить, приемлемы ли недостатки для вашей ситуации. Однако преимущество заключается в том, что конфигурация основана на соглашении, и для добавления новых типов IMessage и IMessageHandler не потребуется дополнительная конфигурация Windsor.

Я использовал комбинацию TypedFactoryFacility Виндзора вместе с пользовательской ITypedFactoryComponentSelector. Используя фабрику, я убираю необходимость в вашем коде напрямую вызывать метод контейнера Resolve. Когда класс должен получить IMessageHandler на основе типа сообщения, он может просто зависеть от фабрики. Вот фабрика:

public interface IMessageHandlerFactory
{
    IMessageHandler GetMessageHandler(string messageType);
}

Поскольку я использую TypedFactoryFacility Виндзора, мне не нужно реализовывать этот метод, но когда дело доходит до вызова метода GetMessageHandler, я "помогу" Виндзору выбрать правильный компонент, определив пользовательский компонент селектор:

public class HandlerTypeSelector : DefaultTypedFactoryComponentSelector
{
    private readonly WindsorContainer container;

    public HandlerTypeSelector(WindsorContainer container)
    {
        this.container = container;
    }

    protected override string GetComponentName(MethodInfo method, object[] arguments)
    {
        if (method.Name == "GetMessageHandler")
        {
            var type = arguments[0].ToString();
            var messageHandlers = container.ResolveAll<IMessageHandler>();
            var single = messageHandlers.SingleOrDefault(h => h.GetMessageType() == type);
            if( single != null)
                return single.GetType().FullName;
        }

        return base.GetComponentName(method, arguments);
    }
}

Зарегистрировать все это так же просто, как:

var container = new WindsorContainer();
container.AddFacility<TypedFactoryFacility>();

container.Register(
    Component.For<IMessageHandlerFactory>().AsFactory(c => c.SelectedWith(new HandlerTypeSelector(container))),
    AllTypes.FromThisAssembly().BasedOn<IMessage>().WithService.AllInterfaces(),
    AllTypes.FromThisAssembly().BasedOn<IMessageHandler>().WithService.AllInterfaces()
    );

Вот некоторый тестовый код, который я использовал для проверки:

var sampleMessage = new RegisterUserMessage();
var factory = container.Resolve<IMessageHandlerFactory>();

var handler = factory.GetMessageHandler(sampleMessage.MessageType);

И классы / интерфейсы, с которыми я тестировал:

public interface IMessage
{
    string MessageType { get; }
}

public interface IMessageHandler
{
    string GetMessageType();
}

public class RegisterUserMessage : IMessage
{
    public string MessageType
    {
        get { return "RegisterUser"; }
    }
}

public class RegisterUserMessageHandler : IMessageHandler
{
    public string GetMessageType()
    {
        return "RegisterUser";
    }
}

public class RemoveUserMessage : IMessage
{
    public string MessageType
    {
        get { return "RemoveUser"; }
    }
}

public class RemoveUserMessageHandler : IMessageHandler
{
    public string GetMessageType()
    {
        return "RemoveUser";
    }
}    

Теперь, недостаток: внутри селектора компонентов я решаю все IMessageHandler и затем решаю, какой из них использовать, основываясь на имени (так как я не назвал обработчики сообщений при регистрации их, они будут зарегистрированы в Виндзоре с их полным именем). Если ваши обработчики сообщений легковесны и / или их немного, или они статичны, это может не быть проблемой. Если они временные и / или имеют значительный код инициализации, их разрешение каждый раз, когда вам нужен один, может быть дорогостоящим. В этом случае я бы отказался от всего этого подхода и пошел бы с чем-то более сложным, но позволяющим разрешить один обработчик сообщений напрямую по имени.

0 голосов
/ 12 мая 2012

Возможно, это немного поздно, но Вы могли бы сделать так:

foreach (string messageType in allMessages)
{
    container.Register(
        Component.For<IMessageHangler>()
        .ImplementedBy(GetImplementationFor(messageType))
        .Named(messageType);
    );
}

Type GetImplementationFor(string messageType)
{
    switch (messageType)
    {
        case "com.business.product.RegisterUser": return typeof(RegisterUserService);
        // more ....
        default: throw new Exception("Unhandled message type!");
    }
}

// In work:
void ProcessMessage(IMessage message)
{
    var handler = this.Container.Resolve<IMessageHandler>(message.MessageType);
    handler.handle(message);
}

Но убедитесь, что с этим подходом Вы не сможете зарегистрировать любой другой компонент ни с одним сообщением.имена типов, например,

container.Register<IFoo>()
.ImplementedBy<FooHandler>()
.Named("com.business.product.RegisterUser");

вызовет исключение, несмотря на то, что IFoo не IMessageHandler.

...