7

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

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

我认为该解决方案可能与 IModelBinderProvider 或 IModelBinder 的实现有关,但是在从网上的示例代码片段中试验了这些东西之后,我仍在寻找一个完整的工作示例,具有可下载的可执行代码而没有任何遗漏一块如何把所有的东西放在一起。

如果您正在寻找有关如何填充选择列表的替代讨论,例如使用“Dependecy Lookup”而不是“Dependecy Injection”,您可能需要查看以下讨论:Best way to populate SelectList for ViewModel on GET/POST Best在 GET/POST 上为 ViewModel 填充 SelectList 的方法

几天前,我在那个线程中写了以下关于我现在在这个线程中寻找的“依赖注入”的后续问题: https ://stackoverflow.com/a/8674525/310457 (它提供了一个关于我正在寻找解决方案的问题)

但是,我没有希望有人会找到标题不太具体的旧线程,而是创建了这个新问题,其中包含一个关于我正在寻找的更具体的主题。我还将为任何想要跟进我正在寻找的特定解决方案的人提供从该线程到这个新问题的链接。

4

1 回答 1

10

我假设你想让你的 ViewModels 通过它们的构造函数自动注入一些东西——例如某种配置对象,视图将用来确定要显示的内容。我还假设当 MVC 尝试从控制器操作的参数自动创建和绑定模型实例时,这种方法会导致“没有为此对象定义无参数构造函数”错误。然后假设我们将使用 DI 框架在运行时自动将 SiteConfig 对象注入到我们的控制器中。

这意味着我们唯一需要解决的问题是,当它们被自动绑定时,如何将注入的对象从我们的 Controller 中获取到其 Actions 的 ViewModel 中。

所以让我们定义一个基础模型供其他人继承。

基础视图模型

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

    public BaseViewModel(ISiteConfig siteConfig)
    {
        this.SiteConfig = siteConfig;
    }
}

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

索引视图模型

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

    public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}

现在让我们定义一个基础控制器,我们的控制器将从中继承。

基本控制器

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

    private readonly ISiteConfig _siteConfig;

    public ISiteConfig SiteConfig
    {
        get
        {
            return _siteConfig;
        }
    }
}

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

家庭控制器

public HomeController: BaseController
{
    public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}

假设我们将 Ninject 用于 DI,Ninject 将被配置为自动创建控制器并ISiteConfig在运行时将具体对象传递给它的构造函数。

现在我们将我们的动作添加到控制器中。

索引操作

public ActionResult Index(IndexViewModel model)
{
    return View(model);
}

因此,如果您尝试调用索引操作,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);
        }
    }
}

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

protected void Application_Start()
{
    ...
    ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}

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

于 2014-06-11T15:10:03.193 回答