繁体   English   中英

我如何使用依赖注入正确?

[英]How do i use dependency injection correct?

所以,这是我第一次使用Ninject,一切都很好,直到我遇到一个问题,其中一些服务类需要在构造函数中添加ModelState参数。 通过回答这个问题解决了问题: 回答上一个问题

现在接下来的问题是我有大约40个服务类需要在构造函数中访问ModelState,因此我选择使用泛型,我真的不知道这是否真的是合法的? 还是我必须重做它?

private readonly IServiceFactory<IAccountService, AccountService> service;
public AccountController(IServiceFactory<IAccountService, AccountService> asf)
{           
  this.service = asf;
}

来自Ninject的RegisterServices:

private static void RegisterServices(IKernel kernel)
{
    kernel.Bind(typeof(IServiceFactory<,>)).To(typeof(ServiceFactory<,>));
}

泛型:

public interface IServiceFactory<T, Y> where T : class  where Y : class
{
     T Create(ModelStateDictionary modelState);
}

public class ServiceFactory<T, Y> : IServiceFactory<T, Y>
    where T : class
    where Y : class
{

    public T Create(ModelStateDictionary modelState)
    {
        var x = (T) Activator.CreateInstance(typeof (Y), new ModelStateWrapper(modelState));
        return x;
    }
}

我试图实现的目标是,我想避免创建40个工厂接口和40个因素,以使exatcly成为同样的东西。 我的解决方案有效,但它实际上是否支持依赖注入? 我真的无法弄明白。

如果您在上一个问题中注意到我的答案的最后一部分,您也可以直接通过IAccountService接口将引用传递给ModelState。 在这种情况下,这会简化事情,但鉴于您之前的问题缺乏背景,不清楚哪个是更好的选择。

public class AccountController : Controller
{
    private readonly ILanguageService languageService;
    private readonly ISessionHelper sessionHelper;
    private readonly IAccountService accountService;

    public AccountController(ILanguageService languageService, ISessionHelper sessionHelper, IAccountService accountService)
    {
        if (languageService == null)
            throw new ArgumentNullException("languageService");
        if (sessionHelper == null)
            throw new ArgumentNullException("sessionHelper");
        if (accountService == null)
            throw new ArgumentNullException("accountService");

        this.languageService = languageService;
        this.sessionHelper = sessionHelper;
        this.accountService = sessionHelper;
    }

    [HttpPost]
    public ActionResult PostSomething()
    {

        // Pass model state of the current request to the service.
        this.accountService.DoSomething(new ModelStateWrapper(this.ModelState));

    }
}

这显然比注入40个工厂更容易,并且会使您的DI配置更简单。

这种方法的唯一缺点(如果你可以称之为),你的IAccountService接口现在需要传递IValidationDictionary的实现。 这意味着IAccountService每个具体实现IAccountService必须依赖于ModelState的抽象IValidationDictionary 如果这是设计的期望,那就没问题。

此外,您应该在您的类中放置guard子句,以确保如果DI容器未能提供实例,则将抛出异常。 私有字段中的readonly关键字使得无法更改构造函数之外的那些字段的值。 基本上,这两个组合在一起可以保证您将拥有一个在构造函数中初始化的实例,并在整个对象生命周期内保持不变。

最后的想法

DI本质上是复杂的。 如果您想获得一个好的答案,您应该提供有关该问题的更多背景信息。 我意识到SO说你应该发布最低要求的代码,但是由于其复杂性,关于DI的问题通常需要比其他问题更多的回答。

如果我知道你试图解决40个类似服务的问题,或者已经知道使用依赖关系的IAccountService成员的相关部分(你还没有提供),我的回答可能会有所不同。 如果你提供更多的上下文,它仍然可能会改变,但我可以对你想要实现的目标做出很多假设。 如果填写空白,它会有很大帮助。

依赖注入将具体实现映射到抽象/接口 ,并将该实例传递给依赖类的构造函数。

制图:

kernel.Bind<IServiceAccount>().To<ServiceAccount>();

Depentandt课程:

private readonly IServiceAccount _serviceAccount;
public AccountController(IServiceAccount serviceAccount)
{
 _serviceAccount = serviceAccount;
}

在您的情况下,您似乎应该重新考虑您的服务工作方式,除非您不为您使用的每个服务创建工厂,您是否需要构造函数上的模型状态?,也许您可​​以将其更改为服务内部的方法如果您的工作流程不允许无效状态抛出异常,则使用它。

如果您需要使用运行时值来创建服务实例,则需要为其创建工厂。

public class ValidServiceAccount : IServiceAccount
{}

public class InvalidServiceAccount : IServiceAccount
{}

public interface IServiceAccountFactory
{
 IServiceAccount Create(bool modelState);
}

public class ServiceAccountFactory : IServiceAccountFactory
{
 public IServiceAccount Create(bool modelState)
 {
  return modelState ? new ValidServiceAccount() : new InvalidServiceAccount();
 }
}

而不是在yoru控制器构造函数上传递IServiceAccount而是传递一个IServiceAccountFactory,您可以看到这可以轻松地为您的项目增加很多复杂性,但是当您真正需要灵活性时,它确实会得到回报。

注意:在asp.net MVC项目中你应该得到ninject控制器工厂,所以它会为你处理构造函数(我认为这个包叫做Ninject.MVC)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM