人们什么时候想在 IModelBinder 上使用 IDependencyResolver?

When does one want to use a IDependencyResolver over a IModelBinder?

创建新项目时,将信息从表单发回控制器时,提示找不到无参数构造函数。这是预期的,因为用作视图模型的视图模型取决于域模型对象。

然后我决定编写自己的模型活页夹。

NewItemViewModelBinder

public class NewItemViewModelBinder : DefaultModelBinder {
    public NewItemViewModelBinder(IKernel kernel) {
        if (kernel == null) throw new ArgumentNullException("kernel");
        this.kernel = kernel;
    }

    protected override object CreateModel(ControllerContext controllerContext
        , ModelBindingContext bindingContext, Type modelType) {
        return kernel.Get(modelType);
    }

    private readonly IKernel kernel;
}

在 NinjectWebCommon.RegisterServices 方法中将此绑定器注册到 ModelBinders.Binders 后,使用模型绑定器的解决方案工作得很好。

public void RegisterServices(IKernel kernel) {
    CompositionRoot.ComposeObjectGraph();
    ModelBinders
        .Binders
        .Add(typeof(NewItemViewModel), new NewItemViewModelBinder(kernel));
}

此外,我还看到了其他一些讨论 DependencyResolver 的帖子。所以我想,如果我可以编写一个可以解决所有其他创建问题的依赖项解析器,那么剩下的我就没有麻烦了。

NinjectDependencyResolver

public class NinjectDependencyResolver : NinjectDependencyScope
    : System.Web.Http.Dependencies.IDependencyResolver
    , System.Web.Mvc.IDependencyResolver {
    public NinjectDepencyResolver(IKernel kernel
        , IDependencyScopeFactory factory) : base(kernel) {
        if (kernel == null) throw new ArgumentNullException("kernel");
        if (factory == null) throw new ArgumentNullException("factory");
        this.kernel = kernel;
    }

    public IDependencyScope BeginScope() { 
        return factory.Create(kernel.BeginBlock()); 
    }

    public object GetService(Type serviceType) {
        return kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType) {
        return kernel.GetAll(serviceType);
    }

    public void Dispose() { base.Dispose(); }

    private readonly IKernel kernel;
    private readonly IDependencyScopeFactory factory;
}

并且在将这个新的解析器设置为 MVC 的依赖解析器之后,

DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));

没用,我遇到了和无参数构造函数一样的问题

所以,我有三个问题。

  1. 我对 DependencyResolver 方法做错了什么?
  2. 与 ModelBinder 相比,使用 DependencyResolver 有什么好处?
  3. 什么时候使用其中一个?

根据 Wrox Professional ASP.NET MVC 4,第 308 页,您不应将 IDependencyResolver 用于您的应用程序。

SHOULD YOU CONSUME DEPENDENCYRESOLVER IN YOUR APPLICATION? You might be tempted to consume IDependencyResolver from within your own application. Resist that temptation. The dependency resolver interface is exactly what MVC needs — and nothing more. It’s not intended to hide or replace the traditional API of your dependency injection container. Most containers have complex and interesting APIs; in fact, it’s likely that you will choose your container based on the APIs and features that it offers more than any other reason.

IDependencyResolver 旨在为 MVC 框架提供依赖项,而不是为您的应用程序提供依赖项。

IDependencyResolver 的实现方式遵循 service locator (anti-)pattern.

此外,IDependencyResolver 不需要通过 MVC 使用 DI。更好的替代方法是使用 IControllerFactory 将依赖项注入控制器并使用其他扩展点(例如 IModelBinder),使用构造函数注入,而不是服务位置。

至于使用 IModelBinder,您在示例中使用了良好的实践,因为您正在执行构造函数注入(尽管有些人可能会争辩说模型不应该具有依赖性,但这取决于您的框架设计)。

作为开发人员,我们总是倾向于尝试尽可能多地概括设计,毕竟,这通常是最好的做法。然而,当谈到 DI 时,我们必须克制这种冲动。对于 DI,最好让每个 class 明确地按类型请求其自己的依赖项,这样 class 需要什么功能就很明显了。服务定位器处于这个范围的另一端 - 它是一个服务黑盒,可能包含也可能不包含应用程序 运行 所需的所有类型,它使应用程序更难配置。