Dependency Injection composition root and decorator pattern

前端 未结 3 2021
不思量自难忘°
不思量自难忘° 2021-02-12 13:42

I\'m getting StackoverflowException\'s in my implementation of the decorator pattern when using dependency injection. I think it is because I\'m \"missing\" somethi

3条回答
  •  野趣味
    野趣味 (楼主)
    2021-02-12 14:09

    Question: I believe I'm missing a key piece of information, which is leading me to StackoverflowExceptions due to circular dependencies. How do I correctly implement my decorator class while still following dependency injection/inversion of control principles and conventions?

    As was already pointed out the best way to do this is with the following construct.

    container.RegisterType(
        new InjectionConstructor(new ResolvedParameter()));
    

    This allows you to specify how the parameters are resolved by type. You could also do it by name but by type is a cleaner implementation and allows for better checking during compile time as a change or mistype in a string will not be caught. Note that the only minute difference between this code part and the code offered by Mark Seemann is a correction in the spelling of InjectionConstructor. I will not elaborate on this part any more as there is nothing else to add that Mark Seemann has not already explained.


    Second question: What about if I decided I only wanted to apply the logging decorator in certain circumstances? So if I had MyController1 that I wished to have a CustomerServiceLoggingDecorator dependency, but MyController2 only needs a normal CustomerService, how do I create two separate registrations?

    You can do this using the way specified above using the Fluent notation OR using named dependency with a dependency override.

    Fluent

    This registers the controller with the container and specifies an overrload for that type in the constructor. I prefer this approach over the second but it just depends on where you want to specify the type.

    container.RegisterType(
        new InjectionConstructor(new ResolvedParameter()));
    

    Named dependency

    You do this the exact same way, you register both of them like so.

    container.RegisterType("plainService");
    
    container.RegisterType(
        new InjectionConstructor(new ResolvedParameter()));
    

    The difference here is that you use a named dependency instead for the other types that can be resolved using the same interface. This is because the interface needs to be resolved to exactly one concrete type every time a resolve is done by Unity so you can not have multiple unnamed registered types that are registered to the same interface. Now you can specify an override in your controller constructor using an attribute. My example is for a controller named MyController2 and I added the Dependency attribute with the name also specified above in the registration. So for this constructor a CustomerService type will be injected instead of the default CustomerServiceLoggingDecorator type. MyController1 will still use the default unnamed registration for ICustomerService which is type CustomerServiceLoggingDecorator.

    public MyController2([Dependency("plainService")]ICustomerService service) 
    
    public MyController1(ICustomerService service) 
    

    There are also ways to do this when you manually resolve the type on the container itself, see Resolving Objects by Using Overrides. The problem here is that you need access to the container itself to do this which is not recommended. As an alternative you could create a wrapper around the container that you then inject into the Controller (or other type) and then retrieve a type that way with overrides. Again, this gets a bit messy and I would avoid it if possible.

提交回复
热议问题