是否应该注入多个"级别"比需要的还要多吗?

时间:2014-12-15 21:11:57

标签: c# 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());

是否应从多个"等级中注入依赖关系?起来,还是有更好的方法?

3 个答案:

答案 0 :(得分:4)

要取得平衡。

一方面,你有一个思想流派,坚持认为所有的依赖关系必须由班级公开,以及#34;正确的"注射。 (这是一种思想学派,它将服务定位器视为一种反模式。)这样做有其优点,但是你发现自己现在处于极致状态。在一些复合模型中,只有正确的复杂性,它们本身就有复合模型,导致聚合根,需要依赖注入,以满足更深层次模型的依赖性。

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

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

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

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

答案 1 :(得分:3)

是的,依赖注入的整个意图就是你compose big object graphs up-front。您可以从Composition Root撰写对象图,这是您的应用程序中具有组合对象图的单一责任的位置。这不是任何特定的控制器,而是一个单独的类,它将控制器与其依赖关系组合在一起。

作文根must have access to all types it needs to compose,除非你想进入后期约束策略(我通常会建议反对,除非有特殊需要)。

答案 2 :(得分:0)

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

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

您的示例使用构造函数注入,这通常是最合适的依赖注入形式:

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

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

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

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

    ...
}

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

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

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

container.Register(Classes.FromAssemblyNamed("Repositories").Pick().WithServiceAllInterfaces());
container.Register(Component.For<IAccountService>().ImplementedBy<AccountService>()); 
container.Register(Component.For<IApplicationDBContext>().ImplementedBy<IApplicationDBContext>()); 
container.Register(Component.For<IApplicationSettingsService>().ImplementedBy<IApplicationSettingsService>()); 
var viewModelService = _container.Resolve<ViewModelService>();

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

祝你好运!