如何为我的应用程序结构中的所有 类 注入足够的信息

How do I inject enough info for all of the classes in my application structure

所以,我正在尝试(但似乎失败了)让我的头脑了解 DI。

我知道我应该编写我的 controls/classes 以便我传递适当的接口并且 IoC 容器将为我解决问题。

而且我可以看到这对于 MVC 控制器是如何工作的。我没有实例化它,系统会向它注入我在构造函数中包含的任何依赖项。

但是链条的下游会发生什么?

我的控制器实例化一个 class (ClassA),它实例化一个 class (ClassB),它需要一个存储库。

ATM,我的代码看起来像...

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

现在,我很确定听众对此会大吃一惊,尤其是因为我必须在我的结构的每个级别包含对 System.Web.Mvc 的引用,但我不确定如何否则我可以这样做,除非我有这样的东西......

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;
    }
}

我的问题是这意味着我将直接或通过工厂将东西注入控制器)它不应该知道任何事情(在这种情况下是回购协议)并且感觉甚至 更多 错误。

我无法解决我的问题 use DI properly 同时,防止在不应该出现的地方暴露系统的位。

我将不胜感激 任何 帮助我充分理解整个 DI 事情,以便我可以解决我的困境或能够解释为什么我不能吃蛋糕 吃掉它

您可以让 IoC 容器也将 ClassA 的实现注入到控制器中。如果这样做,则无需将 IMyRepo 提供给 ClassA。

这当然需要 ClassA 实现一个接口,并且您将其注册到容器中。

如果您不希望容器为您提供 ClassA,另一种可能的方法是让 ClassA 请求服务定位器来解析它的依赖关系。

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

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

这可行,但服务定位器模式被认为是一种反模式。我建议让所有的依赖程序都针对一个接口。

如果您使用的是 IOC 容器,则可以从链的最顶端利用它,例如示例中的 MVC 控制器。假设您的容器可以成功构建所有依赖项,控制器将不知道 ClassB.

你的问题是IOC的部分采用。假设 ClassA 是 'service' class,在 Index().

中更新它是没有意义的

通常它更类似于:

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

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

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

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

如果您担心我为什么要构建 Index() 需要的 ClassA,而我在 Post() 上不使用 ClassA。这通常通过注入工厂来解决。大多数 IOC 容器都提供开箱即用的支持。

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()
    }
}

如果您实际上并不使用它,则无需支付构建 ClassA 的任何费用。

您的控制器应该有几个参数,所有实例化的参数都相同 'services'。如果您发现需要具有 5、7、12 和更多依赖项的构造函数,则表明系统设计不当。

只注入他们需要的类:

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;
    }
}

在应用程序的Composition Root中,您可以组成整个对象图:

var ctrl =
    new MyController(
        new ClassA(
            new ClassB(
                new MyRepo())));