简体   繁体   中英

How to inject different implementations to same interface without conditional/context DI?

I have the following problem and I currently have a solution using conditional dependency injection. I have read that this is a bad idea, such as here in the SimpleInjector docs. I have read a large number of posts now and have seen various things suggesting using Strategy, Factory patterns, etc. What I am really looking for is some specifics - ie an example of some code - about how to solve without conditional injection. I need more that "use a factory". Here's a simplified version of my code. This is in an MVC web app, thus the controllers.

public abstract class QAControllerBase : Controller
{
    protected readonly QABusinessLayer _blQA;
    public QAControllerBase(QABusinessLayer bl) 
    {
        _blQA = bl;
    }

    [HttpGet]
    public ActionResult PPR(string accession, string site)
    {
        var m = _blQA.popPPRViewModel(accession);

        return View(m);
    }
}

public class QASouthController : QAControllerBase
{

    public QASouthController([QASouthBinding] QABusinessLayer bl) : base(bl)
    {

    }

    // some actions that are specific to South
}


public class QANorthController : QAControllerBase
{

    public QANorthController([QANorthBinding] QABusinessLayer bl) : base(bl)
    {

    }

    // some actions that are specific to North
}

public abstract class QABusinessLayer
{
    protected readonly IFullBaseRepo _repo;
    public QABusinessLayer(IFullBaseRepo repo)
    {
        _repo = repo;
    }

    public abstract PPRViewModel popPPRViewModel(string accession);

    protected PPRViewModel DoSomeCommonStuff(PPRViewModel model)
    {
        ...
        return model;
    }

}

public class SouthBusinessLayer: QABusinessLayer
{

    public SouthBusinessLayer([QASouthBinding] IFullBaseRepo repo) : base(repo)
    {

    }

    public override PPRViewModel popPPRViewModel(string accession)
    {
        var m = new PPRViewModel();
        // do some stuff that is specific to South
        DoSomeCommonStuff(m);

        return m;
    }
}

public class NorthBusinessLayer : QABusinessLayer
{


    public NorthBusinessLayer([QANorthBinding] IFullBaseRepo repo) : base(repo)
    {

    }
    public override PPRViewModel popPPRViewModel(string accession)
    {
        var m = new PPRViewModel();
        // do some stuff that is specific to North
        DoSomeCommonStuff(m);

        return m;
    }
}

and here is the Ninject binding code that is pertinent:

            kernel.Bind<QABusinessLayer>()
            .To<SouthBusinessLayer>()
            .WhenTargetHas<QASouthBinding>()
            .InRequestScope();

        kernel.Bind<QABusinessLayer>()
            .To<NorthBusinessLayer>()
            .WhenTargetHas<QANorthBinding>()
            .InRequestScope();

The QASouthBinding and QANorthBinding are just simple attributes. I am not asking for Ninject specific example. Any code sample as to how this might be handled without using conditional or context based injection as I am now.

Make your QABusinessLayer class abstract.

Change your startup configuration to:

kernel
    .Bind<SouthBusinessLayer>()
    .To<SouthBusinessLayer>()
    .InRequestScope();

kernel
    .Bind<NorthBusinessLayer>()
    .To<NorthBusinessLayer>()
    .InRequestScope();

Change your controller constructors to accept a concrete business layer type:

public class QANorthController : QAControllerBase
{
    public QANorthController(NorthBusinessLayer businessLayer) : base(businessLayer)
    {
    }
}

public class QASouthController : QAControllerBase
{
    public QASouthController(SouthBusinessLayer businessLayer) : base(businessLayer)
    {
    }
}

Few things:

  1. If Ninject auto-binds concrete types to the same type, then you don't need to manually configure the dependencies during startup.
  2. You may want to use an interface rather than just passing your concrete BusinessLayer type.

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