2013-08-01 29 views
5

Chúng tôi có rất nhiều trình xử lý lệnh chung được đăng ký bởi Autofac theo kiểu mở chung. Chúng tôi có vài trang trí trang trí tất cả các tay cầm. Bây giờ tôi cần phải đăng ký một trang trí cho chỉ một trình xử lý lệnh và không ảnh hưởng đến tất cả các trình xử lý lệnh khác. Đây là nỗ lực của tôi về điều đó, nhưng tôi dường như không nhận được quyền đăng ký.Đăng ký bộ trang trí Autofac cho chỉ một bộ xử lý lệnh chung

Đây là mã kiểm tra đơn giản tương tự như mã của chúng tôi:

Chúng tôi có hàng trăm các lệnh làm việc như thế này:

class NormalCommand : ICommand { } 

// This command handler should not be decorated 
class NormalCommandHandler : ICommandHandler<NormalCommand> 
{ 
    public void Handle(NormalCommand command) { } 
} 

Và tôi muốn quấn CHỈ TestCommandHandler trong trang trí TestCommandHandlerDecorator

class TestCommand : ICommand { } 

// And I would like to put decorator around this handler 
class TestCommandHandler : ICommandHandler<TestCommand> 
{ 
    public void Handle(TestCommand command) { } 
} 

// This decorator should be wrapped only around TestCommandHandler 
class TestCommandHandlerDecorator : ICommandHandler<TestCommand> 
{ 
    private readonly ICommandHandler<TestCommand> decorated; 

    public TestCommandHandlerDecorator(ICommandHandler<TestCommand> decorated) 
    { 
     this.decorated = decorated; 
    } 

    public void Handle(TestCommand command) 
    { 
     // do something 
     decorated.Handle(command); 
     // do something again 
    } 
} 

Đó là cách tôi đăng ký thành phần của mình:

static class AutofacRegistration 
{ 
    public static IContainer RegisterHandlers() 
    { 
     var builder = new ContainerBuilder(); 

     //Register All Command Handlers but not decorators 
     builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(AutofacRegistration))) 
      .Where(t => !t.Name.EndsWith("Decorator")) 
      .AsClosedTypesOf(typeof(ICommandHandler<>)) 
      .InstancePerLifetimeScope(); 

     // and here is the battle! 
     builder.RegisterType<TestCommandHandler>() 
       .Named<ICommandHandler<TestCommand>>("TestHandler") 
       .InstancePerLifetimeScope(); 

     // this does not seem to wrap the decorator 
     builder.RegisterDecorator<ICommandHandler<TestCommand>>(
      (c, inner) => new TestCommandHandlerDecorator(inner), 
      fromKey: "TestHandler") 
       .Named<ICommandHandler<TestCommand>>("TestHandler1") 
       .InstancePerLifetimeScope(); 

     return builder.Build(); 
    } 
} 

Và đây là cách tôi cố gắng xác nhận rằng tôi nhận được trường hợp đúng đắn về xử lý lệnh/trang trí:

class AutofacRegistrationTests 
{ 
    [Test] 
    public void ResolveNormalCommand() 
    { 
     var container = AutofacRegistration.RegisterHandlers(); 

     var result = container.Resolve<ICommandHandler<NormalCommand>>(); 

     // this resolves correctly 
     Assert.IsInstanceOf<NormalCommandHandler>(result); // pass 
    } 

    [Test] 
    public void TestCommand_Resolves_AsDecorated() 
    { 
     var container = AutofacRegistration.RegisterHandlers(); 

     var result = container.Resolve<ICommandHandler<TestCommand>>(); 

     // and this resolves to TestCommandHandler, not decorated! 
     Assert.IsInstanceOf<TestCommandHandlerDecorator>(result); // FAILS! 
    } 
} 

Khi bình luận nói, trang trí không nhận được áp dụng, đăng ký trang trí được bỏ qua.

Bất kỳ id nào cách đăng ký trang trí này ?? Tôi đang làm gì sai?

+0

Tôi có thể cung cấp một giải pháp sử dụng một container DI , hoặc bạn đang nối với Autofac? – Steven

+0

Tôi đang nối với Autofac ngay bây giờ, nhưng nếu bạn có thể đưa ra ví dụ trong Bản đồ Cấu trúc hoặc Windsor, tôi cũng sẽ quan tâm để xem xét điều đó. Vì mục đích giáo dục. – trailmax

Trả lời

3

Sau smacking đầu tôi chống lại bàn phím đủ thời gian, tôi đã có một số loại giải pháp cho vấn đề của tôi:

static class AutofacRegistration 
{ 
    public static IContainer RegisterHandlers() 
    { 
     var builder = new ContainerBuilder(); 

     builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(AutofacRegistration))) 
      .AsClosedTypesOf(typeof(ICommandHandler<>)) 
      .InstancePerLifetimeScope(); 

     builder.RegisterType<TestCommandHandler>() 
       .Named<ICommandHandler<TestCommand>>("TestHandler") 
       .InstancePerLifetimeScope(); 

     // this works! 
     builder.Register(c => new TestCommandHandlerDecorator(c.ResolveNamed<ICommandHandler<TestCommand>>("TestHandler"))) 
       .As<ICommandHandler<TestCommand>>() 
       .InstancePerLifetimeScope(); 

     return builder.Build(); 
    } 
} 

Ở đây tôi không sử dụng chức năng trang trí của Autofac và gói trang trí bằng tay. Vì vậy, nếu số lượng phụ thuộc trong trình trang trí phát triển, tôi sẽ cần cập nhật vùng chứa để giải quyết tất cả các phụ thuộc bắt buộc.

Nếu bạn biết giải pháp tốt hơn, vui lòng cho tôi biết!

1

Tôi không thể đưa ra bất kỳ ví dụ nào về Castle Windsor hoặc StructureMap và theo kinh nghiệm của tôi, rất khó áp dụng các trang trí mở chung bằng cách sử dụng bất kỳ thứ gì khác ngoài Autofac và Simple Injector. Khi áp dụng có điều kiện trang trí mở chung chung (kịch bản cụ thể của bạn) AFAIK Simple Injector là container DI duy nhất với hỗ trợ gốc cho việc này.

Với Injector đơn giản, bạn đăng ký tất cả các bộ xử lý lệnh của bạn như sau:

container.RegisterManyForOpenGeneric(
    typeof(ICommandHandler<>), 
    typeof(ICommandHandler<>).Assembly); 

trang trí có thể được đăng ký như sau:

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(CommandHandlerDecorator1<>)); 

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(TestCommandHandlerDecorator)); 

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(CommandHandlerDecorator2<>)); 

trang trí được thêm vào theo thứ tự chúng được đăng ký, có nghĩa là trong trường hợp trên, CommandHandlerDecorator2<T> sẽ kết thúc tốt đẹp TestCommandHandlerDecorator kết thúc tốt đẹp CommandHandlerDecorator1<T> bao bọc bất kỳ trình xử lý lệnh cụ thể nào. Vì TestCommandHandlerDecorator là dành cho một ICommandHandler<T> cụ thể, nó chỉ được bao quanh các loại đó. Vì vậy, trong trường hợp của bạn, bạn đã hoàn tất sau khi thực hiện đăng ký trước đó.

Nhưng trường hợp của bạn thực sự là một trường hợp đơn giản.Đơn giản Injector hỗ trợ nhiều thú vị hơn kịch bản, chẳng hạn như có điều kiện áp dụng trang trí dựa trên một vị hoặc một loại hạn chế chung:

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(SomeDecorator<>), c => 
     c.ServiceType.GetGenericArguments()[0] == typeof(TestCommand)); 

Bằng cách cung cấp một vị đến RegisterDecorator bạn có thể kiểm soát nếu một trang trí được áp dụng cho một đăng ký nhất định .

Một tùy chọn khác là áp dụng các ràng buộc kiểu chung cho trang trí. Đơn giản Injector là khả năng xử lý những hạn chế kiểu chung chung:

// This decorator should be wrapped only around TestCommandHandler 
class TestCommandHandlerDecorator<T> : ICommandHandler<T> 
    where T : TestCommand // GENERIC TYPE CONSTRAINT 
{ 
    // ... 
} 

này rất hữu ích khi bạn có bất kỳ xử lý lệnh để xử lý lệnh xuất phát từ TestCommand, nhưng thường bạn sẽ thấy rằng các lệnh thực hiện một hoặc nhiều giao diện và trang trí là áp dụng cho các trình xử lý lệnh xử lý một lệnh với một trong các giao diện đó.

Nhưng một trong hai cách, các trang trí đơn giản có thể được đăng ký như sau:

container.RegisterDecorator(
    typeof(ICommandHandler<>), 
    typeof(TestCommandHandlerDecorator<>)); 

Mặc dù tôi nghĩ rằng cuối cùng thì bạn có thể làm việc này trong mỗi container, hầu hết các container sẽ làm cho điều này thực sự phức tạp để đạt được. Đây là nơi mà Injector đơn giản vượt trội.

+1

Cảm ơn bạn, Steven, điều đó khá thú vị. Tôi đoán tôi sẽ phải tạo cơ hội cho SimpleInjector và thử nó trong dự án tiếp theo của tôi. Tôi thực sự thích tùy chọn thứ hai với ràng buộc kiểu generic - đây là những gì tôi có trong đầu khi bắt đầu triển khai trong Autofac. – trailmax

3

Để tránh việc đăng ký nhãn hiệu trong câu trả lời @ trailmax của bạn có thể xác định các phương pháp khuyến nông sau:

public static class ContainerBuilderExtensions 
{ 
    public static void RegisterDecorator<TService, TDecorater, TInterface>(this ContainerBuilder builder, 
     Action<IRegistrationBuilder<TService, ConcreteReflectionActivatorData, SingleRegistrationStyle>> serviceAction, 
     Action<IRegistrationBuilder<TDecorater, ConcreteReflectionActivatorData, SingleRegistrationStyle>> decoratorAction) 
    { 
     IRegistrationBuilder<TService, ConcreteReflectionActivatorData, SingleRegistrationStyle> serviceBuilder = builder 
      .RegisterType<TService>() 
      .Named<TInterface>(typeof (TService).Name); 

     serviceAction(serviceBuilder); 

     IRegistrationBuilder<TDecorater, ConcreteReflectionActivatorData, SingleRegistrationStyle> decoratorBuilder = 
      builder.RegisterType<TDecorater>() 
       .WithParameter(
        (p, c) => p.ParameterType == typeof (TInterface), 
        (p, c) => c.ResolveNamed<TInterface>(typeof (TService).Name)) 
       .As<TInterface>(); 

     decoratorAction(decoratorBuilder); 
    } 
} 

Và sau đó sử dụng này như sau:

 builder.RegisterDecorator<TestCommandHandler, TestCommandHandlerDecorator, ICommandHandler<TestCommand>>(
      s => s.InstancePerLifetimeScope(), 
      d => d.InstancePerLifetimeScope()); 
+0

Trông khá tuyệt. Tôi sẽ cho nó đi. Toàn bộ câu hỏi giống như một câu đố không có ứng dụng trong thế giới thực. – trailmax

Các vấn đề liên quan