构造函数注入用作Action方法参数的View Model实例

use*_*457 7 dependency-injection viewmodel constructor-injection asp.net-mvc-3

创建视图模型时,您可以将选项(例如,在下拉列表中使用)填充到视图模型的setter属性中.问题是,当该视图模型稍后作为参数(通过框架!)传递到操作方法时,这些属性值尚未自动重新填充,因此如果由于验证错误需要重新显示表单,则需要再次重新填充这些选项.

我在这个问题中特别要求的一个可能的解决方案是如何使MVC框架使用构造函数注入实例化视图模型,这将为视图模型构造函数提供某种数据访问对象的实现(例如存储库) )当视图请求选项时,它们可用于检索选项(例如在辅助方法"DropDownListFor"中)?

我认为该解决方案可能与IModelBinderProvider或IModelBinder的实现有关,但是在网络上的示例代码片段中对这些事情进行了实验后,我仍在寻找一个完全可行的示例,可下载的可执行代码没有任何遗漏将所有事物放在一起的方法.

如果您正在寻找关于如何填充一个选择列表,例如用"Dependecy查找"而不是"Dependecy注入"你可能想看看下面的讨论中,一些另类的讨论:最好的方式来填充的SelectList的视图模型上的GET/POST 最佳在GET/POST上为ViewModel填充SelectList的方法

几天前,我在该帖子中写了关于"Dependecy Injection"的以下后续问题,我正在寻找这个帖子:https://stackoverflow.com/a/8674525/310457 (它提供了一个代码示例问题我正在寻找解决方案)

但是,我没有希望有人会找到那个标题较少的旧帖子,而是用一个更具体的主题来创建这个新问题,关于我在寻找什么.对于想要跟进我正在寻找的这个特定解决方案的任何人,我还将提供从该线程到这个新问题的链接.

Jam*_*ack 9

我假设你想通过他们的构造函数自动注入你的ViewModel - 例如某种配置对象,View将用它来确定要显示的内容.我还假设当MVC尝试从Controller Action的参数自动创建和绑定模型实例时,此方法导致"为此对象定义无参数构造函数"错误.然后我们假设我们将使用DI框架在运行时自动将SiteConfig对象注入到我们的控制器中.

这意味着我们必须解决的唯一问题是如何在我们的Controller中将注入的对象自动绑定到其Actions的ViewModel中.

因此,让我们为其他人继承基础模型.

BaseViewModel

public class BaseViewModel
{
    public ISiteConfig SiteConfig { get; set; }

    public BaseViewModel(ISiteConfig siteConfig)
    {
        this.SiteConfig = siteConfig;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们创建一个继承它的模型.

IndexViewModel

public class IndexViewModel : BaseViewModel
{
    public string SomeIndexProperty { get; set; }

    public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
Run Code Online (Sandbox Code Playgroud)

现在让我们定义一个控制器将继承的基本控制器.

BaseController

public abstract class BaseController : Controller
{
    protected BaseController(ISiteConfig siteConfig)
    {
        _siteConfig = siteConfig;
    }

    private readonly ISiteConfig _siteConfig;

    public ISiteConfig SiteConfig
    {
        get
        {
            return _siteConfig;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们定义实际的控制器.

HomeController的

public HomeController: BaseController
{
    public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Run Code Online (Sandbox Code Playgroud)

假设我们将Ninject用于DI,Ninject将被配置为自动创建Controller并ISiteConfig在运行时将具体对象传递给其构造函数.

现在我们将Action添加到Controller.

指数行动

public ActionResult Index(IndexViewModel model)
{
    return View(model);
}
Run Code Online (Sandbox Code Playgroud)

因此,如果您尝试调用Index Action,MVC将在没有做任何其他事情的情况下爆炸,因为MVC无法找到不带参数的ViewModel构造函数.

所以,答案.我们需要覆盖默认的ModelBinder.

BaseViewModelBinder

public class BaseViewModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
        {
            var baseControl = controllerContext.Controller as BaseController;
            if (baseControl == null)
            {
                throw new Exception("The Controller must derive from BaseController");
            }

            var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
            bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
            return instance;
        }
        else
        {
            return base.CreateModel(controllerContext, bindingContext, modelType);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我们需要将其设置为默认模型绑定器global.asax.cs:

protected void Application_Start()
{
    ...
    ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
Run Code Online (Sandbox Code Playgroud)

就这样.如您所见,当您立即查看Index Action时,MVC将使用我们的自定义模型绑定器.它将意识到IndexViewModel派生自BaseViewModel,因此将尝试使用它可以在Action的Controller中找到的ISiteConfig来启动IndexViewModel实例(因为Controller派生自BaseController).