1

So, I'm trying (and seemingly failing) to get my head around DI.

I know that I'm supposed to write my controls/classes so that I pass in the appropriate interfaces and the IoC container will work stuff out for me.

And I can see how this would work for, say, an MVC Controller. I don't instantiate this and the system will inject any dependencies in to it that I include in the constructor.

But what happens further down the chain?

My Controller instantiates a class (ClassA) that instantiates a class (ClassB) that needs, say, a Repository.

ATM, I've got code that looks like...

public class ClassB
{
    private IMyRepo repo;
    public ClassB()
    {
        repo = DependencyResolver.Current.GetService<IMyRepo>();
        // ...
    }
}

Now, I'm pretty sure there are going to be gasps in the audience at this, especially as I have to include a reference to System.Web.Mvc at each level of my structure but I'm unsure how else I can do this unless I have something like this ...

public class MyController : MyController
{
    private IMyRepo myrepo;
    public MyController(IMyRepo myRepo)
    {
        this.myRepo = myRepo;
    }

    public ActionResult Index()
    {
        var classA = new ClassA(myRepo);
        classA.DoSomething()
        //...
    }
}

public class ClassA
{
    private IMyRepo repo;
    public ClassA(IMyRepo myRepo)
    {
        this.myRepo = myRepo;
    }

    public void DoSomething()
    {
        var classB = new ClassB(myRepo);
        // ...
    }
}

public class ClassB
{
    private IMyRepo repo;
    public ClassB(IMyRepo myRepo)
    {
        this.myRepo = myRepo;
    }
}

My problem with this is it means that I'm going to be injecting stuff in to the Controller either directly or via a Factory) that it should't know anything about (the repos in this case) and that feels even more wrong.

I can't resolve how I can use DI properly and at the same time, prevent the necessity of exposing bits of the system where they aren't supposed to be.

I would appreciate any help in understanding the whole DI thing enough that I can solve my dilemma or be able to explain why I can't have my cake and eat it

Stuart Hemming
  • 1,553
  • 2
  • 21
  • 44

3 Answers3

1

Inject into the classes only what they need:

public class MyController : MyController
{
    private ClassA classA;
    public MyController(ClassA classA)
    {
        this.classA = classA;
    }

    public ActionResult Index()
    {
        classA.DoSomething()
        //...
    }
}

public class ClassA
{
    private ClassB classB;
    public ClassA(ClassB classB)
    {
        this.classB = classB;
    }

    public void DoSomething()
    {
        // ...
    }
}

public class ClassB
{
    private IMyRepo repo;
    public ClassB(IMyRepo myRepo)
    {
        this.myRepo = myRepo;
    }
}

In the application's Composition Root, you can compose the entire object graph:

var ctrl =
    new MyController(
        new ClassA(
            new ClassB(
                new MyRepo())));
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • I'm using Unity so are you suggesting that the UnityConfig is where I need to compose my graphs? I'm also assuming that in the actual code, _all_ of the constructors should, at least where possible take interfaces as parameters. – Stuart Hemming Mar 23 '16 at 10:29
  • 1
    @StuartHemming You compose your object graph(s) in the application's [Composition Root](http://blog.ploeh.dk/2011/07/28/CompositionRoot). You can use Unity to do so, but I'd *recommend* [Pure DI](http://blog.ploeh.dk/2014/06/10/pure-di) instead. Your constructor can take polymorphic, [concrete](http://blog.ploeh.dk/2012/08/31/ConcreteDependencies), or [primitive dependencies](http://blog.ploeh.dk/2012/07/02/PrimitiveDependencies). – Mark Seemann Mar 23 '16 at 10:45
0

You could have the IoC container also inject the implementation of ClassA into the Controller. If you do this, you would not need to provide the IMyRepo into ClassA.

This of course require that ClassA implement an interface and that you register it with the container.

If you don't want the container to provide you with ClassA, another possible way is to have ClassA request the Service Locator to resolve it's dependencies.

public class ClassA
{
    IMyRepo myRepo;
    public ClassA()
    {
      myRepo = Container.Resolve<IMyRepo>();
    }

    public void DoSomething()
    {
        var classB = new ClassB(myRepo);
        // ...
    }
}

This would work, but the service locator pattern is considered an anti-pattern. I would recommend having all you dependencies program against an interface.

Andy T
  • 10,223
  • 5
  • 53
  • 95
  • The service locator is not an anti-pattern. The specific usage of service location in your illustration is an anti-pattern. – Chris Marisic Mar 22 '16 at 16:19
0

If you're using an IOC container you leverage it from the very top of the chain, such as the MVC controller in your example. Assuming your container can construct all of the dependencies successfully the controller will have no knowledge of ClassB.

Your problem is the partial adoption of IOC. Assuming that ClassA is a 'service' class, it doesn't make sense that you new it up in the Index().

Normally it would be more akin to:

public class MyController : MyController
{
    private IMyRepo myrepo;
    public MyController(IMyRepo myRepo)
    {
        this.myRepo = myRepo;
    }

    public ActionResult Index()
    {
        myrepo.DoSomething()
    }
}

or

public class MyController : MyController
{
    private ClassA classA;
    public MyController(IMyRepo myRepo)
    {
        this.classA = myRepo.CreateA();
    }

    public ActionResult Index()
    {
        classA.DoSomething()
    }
}

If your concern is why should i be constructing ClassA that's needed by Index() when I'm on Post() that doesn't use ClassA. That's commonly solved by injecting factories. Most IOC containers provide support for this out of box.

public class MyController : MyController
{
    private IMyRepo myrepo;
    private Func<ClassA> aFactory;
    public MyController(IMyRepo myRepo, Func<ClassA> aFactory)
    {
        this.myRepo = myRepo;
        this.aFactory = aFactory;
    }

    public ActionResult Index()
    {
        var classA = aFactory.Invoke();
        classA.DoSomething()
    }
}

This eliminates paying any cost of constructing ClassA if you don't actually use it.

Your controllers should have few parameters, same for all instantiated 'services'. If you're finding you need constructors with 5, 7, 12, and more dependencies, that's indicative of bad system design.

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
  • I perhaps oversimplified. My Controllers call a BL class that in turn might reference one or more Repos in the DAL. I don't want other devs short cutting and access the repos directly from within the UI (ie the controller); I want them to use the BL where the BL will access the appropriate Repo(s). I can't see how to prevent that if I have to pass in a repo factory to the Controller – Stuart Hemming Mar 22 '16 at 16:23
  • @StuartHemming - have an interface IClassA, IClassB for your BL and inject that into MyController instead – mike123 Mar 22 '16 at 16:27
  • 1
    @StuartHemming There's pretty much nothing you can do to *bar* developers from circumventing your system design. That's solved by education and code reviews. No matter how elaborately you attempt to work with access modifiers, i could still pull it in via reflection. Access modifiers are just suggestions. – Chris Marisic Mar 22 '16 at 16:27
  • @mike123 that doesn't do anything for halting `new ImUsingThisClassAnyway(too, bad, too, sad)` – Chris Marisic Mar 22 '16 at 16:28
  • So, essentially, I'm going to need to create 2 factories; one for all my BL classes and another for all of my Repos and have them all injected in to the Controllers' constructors. I'm guessing that this will mean that whenever I instantiate a new BL class I'm going to have to pass in the Repo factory so that it can do it's thing. – Stuart Hemming Mar 22 '16 at 16:35
  • `whenever I instantiate a new BL class` using an IOC container, **you** (as a developer) generally would *never* instantiate a class. That's the purpose of the container to be responsible for all instantiation. `I'm going to need to create 2 factories` that's overly vague. Most IOC containers can create the factories for you directly, like in my example above `Func` would be done for you automatically with StructureMap. – Chris Marisic Mar 22 '16 at 18:03
  • If you're using a system nothing at all like MVC, you might have to create your own "composition root" that is responsible for locating the necessary services objects need to activate. – Chris Marisic Mar 22 '16 at 18:03