2016-06-15 1 views
0

의 모든 AsClosedTypesOf 등록 변형을 가져 오기 : 나는 자동으로 ICommandHandler의 모든 구현 <>을 등록 할다음의 클래스/인터페이스를 가정 할 수있는 Autofac 빌더

public interface ICommand 
{ 
} 

public class SomeCommand : ICommand 
{ 
} 

public interface ICommandHandler<T> where T : ICommand 
{ 
    void Handle(T arg); 
} 

public class SomeCommandHandler : ICommandHandler<SomeCommand> 
{ 
    void Handle(SomeCommand arg){ /* do something */ } 
} 

public interface ICommandBus 
{ 
    void RegisterHandler<T>(T t) where T : ICommandHandler<T>; 
    void RegisterHandlerByParam<T2>(ICommandHandler<T2> t2) where T2 : ICommand; 
    void RegisterHandlerMethod<T3>(Action<T3> action) where T3 : ICommand 
} 

public class TheCommandBus : ICommandBus 
{ 
    // implements ICommandBus ... 
} 

. 모든 변형 (Register *)은 Action 매개 변수가 더 유연하고 Handler 인터페이스 (액션 대행자)에 종속되지 않지만 유효한 매개 변수이지만 유효한 솔루션입니다.

builder.RegisterAssemblyTypes(Assembly.LoadFrom("MyAssembly.dll")) 
     .AsClosedTypesOf(typeof(ICommandHandler<>)); 

그래서 나는 모든 구현 등록 :

Autofac 예를 들어, 조립 검사에 따라 유형을 등록하고 일반적인 인터페이스의 발견 구현을 등록 할 수있는 기능이 있습니다. 이제는 모두 자동으로 명령 버스에 등록해야합니다. 어떻게 그럴 수 있습니까?

I (예 OnActivated 동안) 그 라인을 추가하여 수동으로이 작업을 수행 할 수 있습니다

:

builder.RegisterType<TheCommandBus>().As<ICommandBus>().OnActivated(args => 
     { 
      // now I need to list all implementations here!!! please, no... 
      args.Instance.RegisterHandler<ICommandHandler<SomeCommand>>(args.Context.Resolve<ICommandHandler<SomeCommand>>()); 

      // does not look better to me than before ... 
      args.Instance.RegisterHandlerByParam<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>()) 

      // uses delegate for, but still need to list all variants 
      args.Instance.RegisterHandlerMethod<SomeCommand>(args.Context.Resolve<ICommandHandler<SomeCommand>>().Handle) 
     }); 

을 내가 문제를 등록하는 동안 람다 식에 이러한 유형을 사용하려면, 내가 확인 할 필요가 다른 컴퍼넌트의 기동 처리시의이 예와 같이, 구체적인 형태 하지만 나는 수동으로 모든 것을 나열하고 싶지는 않습니다. 자동으로 이와 같은 것을 원합니다.

모든 ICommandHandler 구현을 catch하고 Register * 메소드로 자동 등록하려면 어떻게합니까?

편집 : 생성자 내부에서 해결하면 또 다른 변종은 자신을 등록 할 수있는 SomeCommandHandler 클래스를 확장하는 것입니다

:

public SomeCommandHandler(ICommandBus commandBus) 
    { 
     // and register here, for example 
     commandBus.RegisterHandlerbyParam(this); 
    } 

나는 AsClosedTypesOf 등록 결과에 AUTOACTIVATE()를 제공해야이 방법 . (가능한 해결책이지만 지금은 "핸들러"는 두 가지 책임이 있습니다 ... 등록 및 처리)

답변

1

이것은 흥미롭고 까다로운 문제입니다. 비 제너릭으로가는 것은 단순한 IEnumerable<T> 해상도이기 때문에 제네릭은 분명히 그 복잡성에 추가됩니다.

하지만 ... 내가 도울 수 있다고 생각합니다. 당신을 활용할 수 있습니다

...

  • RegisterAssemblyTypesOnRegistered 이벤트가 그래서 당신이 실제로 등록 된 것을 볼 수 있습니다.
  • 버스 용 OnActivating event이므로 핸들러 등록을 할 수 있습니다.
  • 등록 된 핸들러 유형 목록을 OnActivating 이벤트로 전달하는 클로저.
  • 버스에서 RegisterHandler 메소드의 닫힌 일반 버전을 생성하기위한 일부 팬시 - 반사 효과 리플렉션.

수행 방법을 보여주는 전체 예제입니다. 참고 RegisterHandler에 대한 ICommandBus 인터페이스를 원래대로 나열된 형식으로 컴파일하지 않았으므로 조금 변경해야했지만 필요에 따라 수정할 수 있어야합니다. 확인을 위해 ScriptCs에서이 항목을 실행했습니다.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Reflection; 
using Autofac; 

public interface ICommand { } 
public class CommandOne : ICommand { } 
public class CommandTwo : ICommand { } 

public interface ICommandHandler<T> where T : ICommand 
{ 
    void Handle(T arg); 
} 

public class CommandOneHandler : ICommandHandler<CommandOne> 
{ 
    public void Handle(CommandOne arg) { } 
} 

public class CommandTwoHandler : ICommandHandler<CommandTwo> 
{ 
    public void Handle(CommandTwo arg) { } 
} 

public interface ICommandBus 
{ 
    IEnumerable<object> Handlers { get; } 
    void RegisterHandler<TCommand, THandler>(THandler handler) 
    where THandler : ICommandHandler<TCommand> 
    where TCommand : ICommand; 
} 

public class CommandBus : ICommandBus 
{ 
    private readonly List<object> _handlers = new List<object>(); 

    public IEnumerable<object> Handlers 
    { 
    get 
    { 
     return this._handlers; 
    } 
    } 

    public void RegisterHandler<TCommand, THandler>(THandler handler) 
    where THandler : ICommandHandler<TCommand> 
    where TCommand : ICommand 
    { 
    this._handlers.Add(handler); 
    } 
} 

var builder = new ContainerBuilder(); 

// Track the list of registered command types. 
var registeredHandlerTypes = new List<Type>(); 
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()) 
    .AsClosedTypesOf(typeof(ICommandHandler<>)) 
    .OnRegistered(e => registeredHandlerTypes.Add(e.ComponentRegistration.Activator.LimitType)); 

// Initialize the bus by registering handlers on activating. 
builder.RegisterType<CommandBus>() 
    .As<ICommandBus>() 
    .OnActivating(e => { 
    foreach(var handlerType in registeredHandlerTypes) 
    { 
     // Due to the generic method, some crazy reflection happens. 
     // First, get ICommandHandler<T> interface. 
     var handlerInterfaceType = handlerType.GetInterface("ICommandHandler`1"); 
     // Grab the <T> from the ICommandHandler<T>. 
     var commandType = handlerInterfaceType.GetGenericArguments()[0]; 
     // Build the closed generic version of RegisterHandler<TCommand, THandler>. 
     var registerMethod = typeof(ICommandBus).GetMethod("RegisterHandler").MakeGenericMethod(commandType, handlerType); 
     // Call the closed generic RegisterHandler<TCommand, THandler> to register the handler. 
     registerMethod.Invoke(e.Instance, new object[] { e.Context.Resolve(handlerInterfaceType) }); 
    } 
    }) 
    .SingleInstance(); 

var container = builder.Build(); 
using(var scope = container.BeginLifetimeScope()) 
{ 
    var bus = scope.Resolve<ICommandBus>(); 
    foreach(var t in bus.Handlers) 
    { 
    // List the handler types registered. 
    Console.WriteLine(t.GetType()); 
    } 
} 
+0

좋은 해결책. 고마워. 나는 "fancy-schmancy reflection"에 대한 2 번째 upvote를 줄 것이다 :-D – Beachwalker