Unity and MVC–Resolving types challenge


Imagine you have a situation like this:

image

2 MVC controllers (A and B) have a dependency on SvcA. SvcA has a dependency on SvcB and a component implementing IX.SvcB has a dependency on something implementing IY.

Every time the system resolves CtrlA we want to provide Y1 and every time it resolves CtrlB, we want to provide Y2.

Something like this:

image

and

image

 

How would you configure Unity to solve this?

No “if() else” allowed. System supplies its own MVC’s ControllerFactory. (via ControllerBuilder.Current.SetControllerFactory). Ideally, we want the ControllerFactory to have no knowledge of any of these specific components.

Some help: read Chris’s blog and Unity docs.

Comments (4)

  1. Abc says:

    IF (ControllerFactory to have no knowledge) THEN

     Write custom extension that will have this "logic" (those IFs for different CTRLs) and will return desired type/instance in special cases, otherwise will allow normal resolution of dependencies.

    ELSE

     use child contaner per each such drawn group class

    Somewhere you you will have to place those if (or translate ifs into some kind of hierarchy (interface/object), but in this case if will exist inside of unity during resolution)

  2. Eugenio says:

    I thought about subcontainers too, but the code seemed to complicated. It could work though, there's no single (correct) answer. Thanks for posting yours!

  3. Nicholas Blumhardt says:

    Hi Eugenio,

    This is pretty straightforward in Autofac, I guess that the same approach can be used in Unity.

    I would be inclined to suggest that it is not good practice to try to dynamically choose between Y1 and Y2 at runtime based on which controller is being instantiated further up the graph. This will be fragile and is best avoided when possible.

    There are several ways to accomplish this, ranging from parameter passing to Multitenancy.

    The simplest solution is to think about components as an abstraction on top of types, so the same type can be used to implement several components. We can distinguish between different components implemented by the same type by giving them names.

    First, we register the 'default' configuration that will be used by controller A. That is, we just register Controller A, Service A, Service B and Y1 as if controller B's configuration is irrelevant. Controller A can just use autowiring, and we can forget about it from hereon in.

    Then, we create named registrations for Service A and Service B, which we'll call "alternate" and configure these to use Y2:

    builder.RegisterType<ServiceA>()

       .Named<ServiceA>("alternate")

       .WithParameter(

           (pi, c) => pi.ParameterType == typeof(ServiceB),

           (pi, c) => c.ResolveNamed<ServiceB>("alternate");

    builder.RegisterType<ServiceB>()

       .Named<ServiceB>("alternate")

       .WithParameter(

           (pi, c) => pi.ParameterType == typeof(IY),

           (pi, c) => c.ResolveNamed<IY>("alternate");

    builder.RegisterType<Y2>()

       .Named<IY>("alternate");

    Named registrations like this aren't picked up in normal dependency wiring, so for all intents and purposes they're invisible to the rest of the container.

    Now, when we register the controllers, we set controller B to use the alternate implementation of service A:

    builder.RegisterControllers()

       .Except<ControllerB>(b => b

           .WithParameter(

               (pi, c) => pi.ParameterType == typeof(ServiceA),

               (pi, c) => c.ResolveNamed<ServiceA>("alternate")));

    Seems fairly long-winded, but this should be a rare situation. If at all possible, in the "real world" I'd be trying to eliminate this situation from the design, but I realise that isn't always possible or desirable.

    Hope this helps.

    Nick

  4. Eugenio says:

    Thanks Nick! I've never tried Autofac, but heard great things about it.