是否应该向dependency injection许多“级别”而不是需要它?

我正在使用SOLID原则编写C#ASP.NET MVC Web应用程序。

我编写了一个ViewModelService ,它依赖于AccountServiceRepositoryService ,所以我在ViewModelServer注入了这两个服务。

PermissionService依赖于HttpContextBase ,以便使用GetOwinContext()来获取UserManager的实例。 控制器有一个需要使用的HttpContextBase实例 – 所以我似乎必须将HttpContextBase实例注入ViewModelService ,然后将其注入PermissionService

所以,就代码而言,我有:

 public ViewModelService public CategoryRepository(ApplicationDbContext context, IPermissionService permissionservice) public AccountService(HttpContextBase httpcontext, IPrincipal securityprincipal) 

为了实例化ViewModelService ,我接着这样做:

 new ViewModelService( new CategoryRepository( new ApplicationDbContext(), new PermissionService( new AccountService(HttpContext, Thread.CurrentPrincipal), new UserPasswordRepository(new ApplicationDbContext()), new ApplicationSettingsService())), new PasswordRepository( new ApplicationDbContext(), new PermissionService( new AccountService(HttpContext, Thread.CurrentPrincipal), new UserPasswordRepository(new ApplicationDbContext()), new ApplicationSettingsService())), new ModelValidatorService()); 

应该从那么多“级别”注入依赖,还是有更好的方法?

要取得平衡。

一方面,你有一个思想学派,坚持所有的依赖关系必须被课堂暴露才能“正确”注入。 (这是一种思想学派,它认为服务定位器之类的东西是一种反模式。)这有其优点,但是你发现自己现在处于极致状态。 在一些复合模型中,只有正确的复杂性本身具有复合模型,导致聚合根,需要大量dependency injection,以满足更深层模型的依赖性。

就个人而言,我发现这会在这种情况下产生耦合。 这是DI打算解决的问题,而不是创造。

另一方面,您有一个允许服务定位器方法的思想流派,模型可以在内部调用一些常见的域服务来解决它的依赖关系。 这有一点好处,但是你发现你的依赖关系不太为人所知,并且如果无法解决任何给定的依赖关系,就有可能出现运行时错误。 (基本上,你可以在更高的层次上获得错误,因为消费对象永远不会知道消费的对象需要一些未提供的东西。)

就个人而言,我经常使用服务定位器方法(主要是因为它是一个非常方便的模式,用于将DI引入遗留域,作为更大的重构练习的一部分,这是我专业做的很多)并且从未遇到过这样的问题。的问题。

无论哪种方式都有阴阳。 我认为每个解决方案空间都有自己的平衡。 如果您发现直接注入使系统难以维护,则可能需要调查服务位置。 相反,如果整个领域模型本身具有内在耦合,并且这个DI问题仅仅是这种耦合的症状而不是其原因,那么也可能值得研究。

是的,dependency injection的整个意图是你预先组成大对象图 。 您可以从合成根组成对象图, 合成根是应用程序中具有组成对象图的单一责任的位置。 这不是任何特定的Controller,而是一个单独的类,它将控制器与其依赖项组合在一起。

组合根必须能够访问它需要编写的所有类型 ,除非你想进入后期绑定策略(我通常建议不要这样做,除非有特殊需要)。

我坚信服务定位器比dependency injection更糟糕。 它们可以是一种有用的遗留技术,也是一种有用的踏脚石,但如果你正在设计一些新东西,那就明确了。

主要原因是服务定位器导致具有隐式依赖性的代码,这使得代码不太清晰并且破坏了封装。 它还可能导致运行时错误,而不是编译时错误和交互测试。

您的示例使用构造函数注入,这通常是最合适的dependency injectionforms:

 public ViewModelService(ICategoryRepository categoryRepository, IPasswordRepository passwordRepository, IModelValidatorService modelValidator) { ... } 

这有明确的依赖关系,这很好。 这意味着您无法在不传递其依赖项的情况下创建对象,如果您尝试将获得编译时错误而不是运行时错误。 它也适用于封装,只需查看类的接口,您就知道它需要哪些依赖项。

您可以使用服务定位器执行此操作,如下所示:

 public ViewModelService() { var categoryRepository = CategoryRepositoryServiceLocator.Instance; var passwordRepository = PasswordRepositoryServiceLocator.Instance; var modelValidator FModelValidatorServiceLocator.Instance; ... } 

这有隐含的依赖关系,你只能通过查看接口来判断,你还必须查看实现(这打破了封装)。 您还可以忘记设置其中一个服务定位器,这将导致运行时exception。

在您的示例中,我认为您的ViewModelService很好。 它引用了抽象(ICategoryRepository等),并不关心如何创建这些抽象。 用于创建ViewModelService的代码有点难看,我建议使用Inversion of Control容器(例如Castle Windsor,StructureMap等)来帮助。

在温莎城堡,您可以执行以下操作:

 container.Register(Classes.FromAssemblyNamed("Repositories").Pick().WithServiceAllInterfaces()); container.Register(Component.For().ImplementedBy()); container.Register(Component.For().ImplementedBy()); container.Register(Component.For().ImplementedBy()); var viewModelService = _container.Resolve(); 

在开始之前,请务必阅读并理解“注册,解析,发布”和“组合根”模式。

祝好运!