简单注入器:用于多个接口的装饰器

时间:2018-11-29 10:44:09

标签: c# dependency-injection decorator simple-injector

我想用简单注入器修饰以下继承(将其重命名以使其更具可读性):

interface IGetData<T,U> { }
interface ICustomerService : IGetData<Customer, Guid> { }
class CustomerServiceImpl : ICustomerService { }

我有一个名为IGetData<T,U>的{​​{1}}装饰器,还有一个名为GetDataDecorator的{​​{1}}装饰器。我的目标是为ICustomerService有两个(链接的)装饰器,一个基于CustomerServicePermissionDecorator接口,一个基于CustomerServiceImpl接口。我在启动时注册了两个装饰器:

IGetData<T,U>

第一次注册工作正常,ICustomerService中的断点表明从container.RegisterDecorator<ICustomerService, CustomerServicePermissionDecorator>(); container.RegisterDecorator(typeof(IGetData<,>), typeof(GetDataDecorator<,>)); 调用了那里的方法。但是,CustomerServiceImpl方法从不执行。

我想这是我的误解-我在做什么错了?

1 个答案:

答案 0 :(得分:3)

在这些复杂的情况下,通常有助于手动编写对象图,因为这样可以使发生的事情更加直观。它甚至允许C#编译器发出不可桥接的问题的信号。

根据您指定的设计,您可以手动构造以下对象图。

ICustomerService impl = new CustomerServiceImpl();

ICustomerService dec1 = new CustomerServicePermissionDecorator(impl);

IGetData<Customer, Guid> dec2 = new GetDataDecorator<Customer, Guid>(dec1);

// Consumer depends on ICustomerService 
var consumer = new Consumer(dec2); <-- compile error

从第三行可以看出,从技术上讲,可以使用ICustomerService装饰器装饰GetDataDecorator<Customer, Guid>。但是,由于GetDataDecorator<T, U>没有实现ICustomerService,因此不可能将该装饰器注入任何需要ICustomerService的使用者中。这就是示例中最后一行代码给出编译错误的原因。

由于无法使用普通的旧C#构造此对象图,因此Simple Injector也将无法执行此操作。它受限于公共语言运行时给出的限制。

但是,在这种情况下,简单注入器比CLR更具限制性,因为前面示例中的任何ICustomerService都可以用GetDataDecorator<Customer, Guid>装饰。可以构造依赖GetData<Customer, Guid>的使用者。但是Simple Injector不允许这样做。

不允许这样做的原因之一是为了防止非常复杂和混乱的情况,在某些情况下使用装饰器,而在另一些情况下省略装饰器。这就是为什么Simple Injector会强制您明确声明应在其上应用装饰器的接口。 Simple Injector将沿继承链寻找基本接口,这似乎是您所期望的行为。

尽管很难对您的设计发表评论,但是您可能要考虑一起删除ICustomerService。特别是因为您已经在使用通用接口。我经常看到开发人员试图通过在通用和非通用之间混合使用旧的SOLID接口(最可能是ICustomerService),但是效果总是不好。您应该全力以赴,放弃过于宽泛的非通用接口。当您这样做时,Simple Injector会为您简化装饰器的应用。