我正在研究Hanselman关于MVC的教程,并尝试在“创建”和“编辑”视图中使用视图模型,以便填写下拉菜单。在此,我们将创建一个表Dinner
,其中包含Title
和Country
列以及相关类,然后是包含dinner
对象和{{{ 1}}用于下拉菜单: -
SelectList
这用于在控制器中创建Edit和Create方法。 Edit方法如下所示: -
public class DinnerFormViewModel {
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
public DinnerFormViewModel (Dinner dinner) {
Dinner = dinner;
Countries = new SelectList(...code to create list of countries..., dinner.Country);
}
}
并且视图中生成的代码如下所示: -
public ActionResult Edit(int id, FormCollection formValues) {
Dinner dinner = dinnerRepository.GetDinner(id);
try {
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinner.DinnerId });
}
catch { ...etc... }
}
到目前为止,这么好。但是当谈到Create方法时,它开始变得有点奇怪。虽然视图中的代码基本相同,但当我调用<div class="editor-field">
<%: Html.EditorFor(model => model.Dinner.Title) %>
<%: Html.ValidationMessageFor(model => model.Dinner.Title) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("Country", Model.Countries) %>
<%: Html.ValidationMessage("Country", "*") %>
</div>
方法来填充UpdateModel
对象时,填充的唯一字段是dinner
,来自拉 - 下菜单;当我使用Country
进行尝试时,DinnerFormViewModel
中的Country
保留Dinner
,但所有其他字段都已填写。因此,Create方法如下所示: -
null
虽然这有效,但对我来说并不像我应该编写此代码的方式。是不对,或者不是,我该怎么办? (教程中给出的例子没有编译,这是一千个怜悯。)
答案 0 :(得分:2)
Html.EditorFor(model => model.Dinner.Title)
这会自动映射到您的视图模型,因为生成的HTML name
将知道如何映射到Dinner.Title
Html.DropDownList("Country", Model.Countries)
这将为HTML元素指定名称“Country”。这意味着它在发布到您的视图模型时需要model.Country
,这在ViewModel上不存在,但在Dinner中确实存在。因此UpdateModel(dinnerFormViewModel);
不起作用,但UpdateModel(dinner);
确实有效。
所有这些都基于为输入元素生成的name
,只要它决定如何分配到视图模型中。我经常在浏览器中检查生成的HTML,以便在POST上映射值时出现问题时检查生成的内容。
您可以使用DropDownListFor
,因此您可以指定model.Dinner.Country
,以便生成完全符合嵌套关系资格的name
。
所以现在你明白为什么它不按照你想要的方式工作。从这里开始,你如何更实际地解决它有许多我见过常用的变体,但大多数都涉及到视图模型到实体的某种映射,并且通常在另一层,如业务层,数据访问层,或利用AutoMapper等工具。
您可以将Country属性添加到ViewModel的根目录。
或者我希望在Dinner
中没有嵌套属性的情况下使视图模型更平坦,并在发布后将视图模型映射到您的实体。看起来Dinner
是您的实体。我从未在我的视图模型中包含我的实体,并且将实体保留在控制器和视图之外是一种非常常见的做法。相反,在GET期间的某个时刻有一个Entity-&gt; ViewModel赋值,然后在post上有一个ViewModel-&gt; Entity映射。当你最终转向分层方法时,这将更有意义。
通过映射我的意思是基本的分配。例如,在检索实体时,您可以将其映射到视图模型,如下所示:
return repo.Get(someId).Select(e =>
new DinnerFormViewModel {
HostName = e.HostName,
// here is an example of flattening out a related entity relationship
CountryId = e.Location.CountryId,
Cost = e.Cost
});
当然我编造了一些属性,因为我不知道你的实体是什么样的。
这允许您重构数据库关系,并且只需要更改映射。