2014-09-23 5 views
1

Вот мой сценарий:Windsor регистрация для общих команд/обработчиков команд

public static class DomainCommandProcessor 
{ 
    public static void Dispatch<T>(T command) where T : IDomainCommand 
    { 
     var serviceLocator = ServiceLocator.Current; 

     var handler = serviceLocator.GetInstance<IDomainCommandHandler<T>>(); 
     if (handler != null) 
      handler.Handle(command); 
    } 
} 

public class FakeGenericCommand<T1, T2> : IDomainCommand 
{ 
    public FakeGenericCommand(T1 first, T2 second) 
    { 
     First = first; 
     Second = second; 
    } 

    public T1 First { get; private set; } 
    public T2 Second { get; private set; } 
} 

public class FakeGenericCommandHandler<T1, T2> : IDomainCommandHandler<FakeGenericCommand<T1, T2>> 
{ 
    public void Handle(FakeGenericCommand<T1, T2> command) 
    { 
     // something interesting 
    } 
} 

Использование:

DomainCommandProcessor.Dispatch(new FakeGenericCommand<string, string>("hi", "there")) 

Я не могу получить регистрацию права Виндзор. Следующее прекрасно работает для всех моих необщих команд:

container.Register(Classes.FromAssemblyNamed(namespaceName) 
    .BasedOn(typeof(IDomainCommandHandler<>)) 
    .WithService.AllInterfaces() 
    .LifestyleTransient()); 

Если зарегистрировать каждую возможную реализацию непосредственно, она работает, но, очевидно, неоптимальные:

container.Register(
    Component.For<IDomainCommandHandler<FakeGenericCommand<string, string>>>() 
     .UsingFactoryMethod(input => new FakeGenericCommandHandler<string, string>()) 
     .LifestyleTransient()); 

Предложение?

+0

Поскольку вы используете библиотеку DI, существует ли какая-либо веская причина для того, чтобы вы прибегли к сервису Location вместо использования Injection Dependency? В чем преимущество использования этого статического «DomainCommandProcessor» над «IDomainCommandProcessor», который вы вводите в компоненты, которые в нем нуждаются? – Steven

ответ

1

То, что вы здесь является учебник случай, нуждающихся в typed factory: Я answered вопрос, где я описал, как использовать его


Этот ответ сильно вдохновлен this article.

Регистрации всех ваши обработчики

container.Register(Classes.FromAssemblyInThisApplication() 
    .BasedOn<IDomainCommandHandler>() 
    .WithServiceAllInterfaces()); 

Объявите фабричный интерфейс, который будет возвращать обработчик для команды

public interface IDomainCommandHandlerFactory 
{ 
    IDomainCommandHandler[] GetHandlersForCommand(IDomainCommand command); 
} 

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

public class HandlerSelector:ITypedFactoryComponentSelector 
{ 
    public TypedFactoryComponent SelectComponent(MethodInfo method, Type type, object[] arguments) 
    { 
     var message = arguments[0]; 
     var handlerType = typeof(IDomainCommandHandler<>).MakeGenericType(message.GetType()); 
     return new TypedFactoryComponentCollection(handlerType.MakeArrayType(), new Arguments(arguments)); 
    } 
} 

Затем скажите Windsor, что вы хотите фабрику, которая вернет IDomainCommandHandler<T>. Не производите код для фабрики.

container.AddFacility<TypedFactoryFacility>(); 
container.Register(Component.For<ITypedFactoryComponentSelector>().ImplementedBy<HandlerSelector>()); 
container.Register(Component.For<IDomainCommandHandlerProvider>().AsFactory()); 

Теперь вы можете использовать завод, чтобы получить ваши обработчик для команды

var provider = container.Resolve<IDomainCommandHandlerFactory>(); 
var msg = new Type2Message(); 
var msgHandler = provider.GetHandlersForCommand(msg.MessageType); 

Пожалуйста, обратите внимание, что обработчики в примере не работают против команды себя, но имеют Execute функцию , Если вы хотите вернуть закрытые общие объекты, вам нужно отбросить их после разрешения, так как вы не можете возвращать разные типы из одного метода.

Я бы посоветовал вам прочитать оригинальную статью, так как он также содержит дополнительную информацию об образе жизни, выпуске компонентов и других интересных моментах.


Вот пример селекторе я использую для запросов, где я задающих запрос и ответ в виде родовых компонентов

protected override Type GetComponentType(MethodInfo method, object[] arguments) 
{ 
    var request = arguments[0].GetType(); 
    var response = arguments[1] as Type; 
    var handlerType = typeof(IHandlerOf<,>).MakeGenericType(request, response); 
    return handlerType; 
} 

И вот результат вызова завода (T является запрос, R ответ)

var handler = handlerFactory.GetHandler<T>(input, typeof(R)); 
var requestType = input.GetType(); 
var responseType = typeof(R); 
var handlerType = typeof(IHandlerOf<,>).MakeGenericType(requestType, responseType); 
r = (R)handlerType.GetMethod("Handle").Invoke(handler, new object[] { input }); 
+0

Мои обработчики команд являются общими, и это похоже на ваш пример, это не так. TypedFactoryComponentCollection также не решает, используете ли вы последнюю версию Windsor? – agartee

+0

IDomainCommandHandlerFactory не имеет дело с общими обработчиками команд – agartee

+0

Действительно, это то, что я заметил. Если вы возвращаете общие обработчики команд, вам нужно сделать некоторое отражение после получения вашего обработчика команд, чтобы назвать его универсальным методом, поскольку фабрика не может возвращать разные закрытые общие типы. – samy

Смежные вопросы