简体   繁体   中英

Simple Injector: Decorator for multiple interfaces

I have the following inheritance I want to decorate with Simple Injector (renamed stuff to make it more readable):

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

I have one decorator for IGetData<T,U> named GetDataDecorator , and I have another decorator for ICustomerService named CustomerServicePermissionDecorator . My goal is to have two (chained) decorators for CustomerServiceImpl , one based on the IGetData<T,U> interface, and one based on the ICustomerService interface. I register both decorators on startup:

container.RegisterDecorator<ICustomerService, CustomerServicePermissionDecorator>();
container.RegisterDecorator(typeof(IGetData<,>), typeof(GetDataDecorator<,>));

The first registration works fine, a breakpoint in CustomerServiceImpl shows that the methods there are called from the CustomerServicePermissionDecorator . However, the GetDataDecorator methods are never executed.

I guess it's a misconception on my side - what am I doing wrong?

In these complex situations, it typically helps to write the object graph by hand, as that makes it much more visual what is going on. It even allows the C# compiler to signal unbridgeable problems.

Based on your specified design, you can construct the following object graph by hand.

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

As you can see in the third line, technically, it would be possible to decorate an ICustomerService with a GetDataDecorator<Customer, Guid> decorator. However, because GetDataDecorator<T, U> does not implement ICustomerService , it is impossible to inject that decorator into any consumer that expects an ICustomerService . This is why the last line of code in the example gives a compile error.

And as this object graph isn't constructible using plain old C#, Simple Injector will also not be able to do this. It is bound to the limitations given by the Common Language Runtime.

Simple Injector, however, is in this case more restrictive than the CLR as any ICustomerService of the previous example can be decorated with an GetDataDecorator<Customer, Guid> . A consumer that depends on GetData<Customer, Guid> could be constructed. But Simple Injector does not allow this.

One of the reasons to disallow this is to prevent very complex and confusing situations, where decorators are applied in some cases, but omitted in other. That's why Simple Injector forces you to explicitly state the interface on which the decorator should be applied. Simple Injector will not walk the inheritance chain to look for base interfaces, which seems to be the behavior you were expecting.

Although it is hard to comment on your design, you might want to consider removing the ICustomerService alltogether. Especially since you are already using a generic interface. I often see developers trying to hang on to their old interfaces (which ICustomerService most likely is) by making a hybrid between the generic and the non-generic, but that hardly ever works out well. You should go full-in and ditch the overly wide, non-generic interfaces. When you do that, Simple Injector will simplify apply decorators for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM