ViewModel 中未填写下拉值
Pulldown values not filled in in ViewModel
我正在学习 Hanselman 的 MVC 教程,并尝试在创建和编辑视图中使用视图模型来填充下拉菜单。在此我们被指示创建一个 table Dinner
,列包括 Title
和 Country
以及相关的 类,然后是一个包含 dinner
对象和下拉菜单的 SelectList
因此:-
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);
}
}
我用它来在控制器中创建 Edit 和 Create 方法。编辑方法如下所示:-
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... }
}
视图中生成的代码如下所示:-
<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>
到目前为止,还不错。但是当涉及到 Create 方法时,它开始变得有点奇怪。虽然视图中的代码基本相同,但当我调用 UpdateModel
方法来填充 dinner
对象时,唯一填充的字段是 Country
,来自 pull-向下菜单;当我尝试使用 DinnerFormViewModel
时,Dinner
中的 Country
被保留 null
,但所有其他字段都已填写。因此 Create 方法如下所示: -
public ActionResult Create(FormCollection formValues) {
Dinner dinner = new Dinner();
DinnerFormViewModel dinnerFormViewModel = new DinnerFormViewModel(new Dinner());
try {
UpdateModel(dinnerFormViewModel);
UpdateModel(dinner);
dinnerFormViewModel.Dinner.Country = dinner.Country;
dinnerRepository.Add(dinnerFormViewModel.Dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinnerFormViewModel.Dinner.DinnerId });
}
catch {...etc...}
}
虽然这行得通,但在我看来这不是我应该编写此代码的方式。这是正确的,如果不是,我应该怎么办? (可惜教程中给出的例子编译不通过。)
Html.EditorFor(model => model.Dinner.Title)
这会自动映射到您的视图模型,因为生成的 HTML name
将知道如何映射到 Dinner.Title
Html.DropDownList("Country", Model.Countries)
这将为 HTML 元素赋予名称 "Country"。这意味着当 post 访问您的视图模型时,它期望 model.Country
,这在您的 ViewModel 中不存在,但在 Dinner 中存在。因此 UpdateModel(dinnerFormViewModel);
不起作用,但 UpdateModel(dinner);
起作用。
这完全基于为输入元素生成的 name
以及它如何决定分配到视图模型中。我经常在浏览器中检查生成的 HTML 以在 POST.
上的映射值出现问题时检查生成的内容
您可以使用 DropDownListFor
,这样您就可以指定 model.Dinner.Country
,这样它将生成完全符合嵌套关系的 name
。
所以现在你明白为什么它没有按照你想要的方式工作了。从这里开始,如何更实际地解决它有许多我见过的常用变体,但大多数都会涉及视图模型到实体的某种映射,并且通常在另一层,如业务层、数据访问层,或利用 AutoMapper 等工具。
您可以将国家 属性 添加到 ViewModel 的根目录。
或者我希望让您的视图模型更平坦,其中属性未嵌套在 Dinner
中,然后 posts,将您的视图模型映射到您的实体。看起来 Dinner
是您的实体。我从不在我的视图模型中包含我的实体,将实体置于控制器和视图之外是一种很常见的做法。相反,在 GET 期间的某个时刻有一个 Entity->ViewModel 赋值,然后在 post 上有一个 ViewModel->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
});
当然我编了一些属性因为我不知道你的实体长什么样
这允许您重构您的数据库关系,您只需要更改您的映射。
我正在学习 Hanselman 的 MVC 教程,并尝试在创建和编辑视图中使用视图模型来填充下拉菜单。在此我们被指示创建一个 table Dinner
,列包括 Title
和 Country
以及相关的 类,然后是一个包含 dinner
对象和下拉菜单的 SelectList
因此:-
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);
}
}
我用它来在控制器中创建 Edit 和 Create 方法。编辑方法如下所示:-
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... }
}
视图中生成的代码如下所示:-
<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>
到目前为止,还不错。但是当涉及到 Create 方法时,它开始变得有点奇怪。虽然视图中的代码基本相同,但当我调用 UpdateModel
方法来填充 dinner
对象时,唯一填充的字段是 Country
,来自 pull-向下菜单;当我尝试使用 DinnerFormViewModel
时,Dinner
中的 Country
被保留 null
,但所有其他字段都已填写。因此 Create 方法如下所示: -
public ActionResult Create(FormCollection formValues) {
Dinner dinner = new Dinner();
DinnerFormViewModel dinnerFormViewModel = new DinnerFormViewModel(new Dinner());
try {
UpdateModel(dinnerFormViewModel);
UpdateModel(dinner);
dinnerFormViewModel.Dinner.Country = dinner.Country;
dinnerRepository.Add(dinnerFormViewModel.Dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id = dinnerFormViewModel.Dinner.DinnerId });
}
catch {...etc...}
}
虽然这行得通,但在我看来这不是我应该编写此代码的方式。这是正确的,如果不是,我应该怎么办? (可惜教程中给出的例子编译不通过。)
Html.EditorFor(model => model.Dinner.Title)
这会自动映射到您的视图模型,因为生成的 HTML name
将知道如何映射到 Dinner.Title
Html.DropDownList("Country", Model.Countries)
这将为 HTML 元素赋予名称 "Country"。这意味着当 post 访问您的视图模型时,它期望 model.Country
,这在您的 ViewModel 中不存在,但在 Dinner 中存在。因此 UpdateModel(dinnerFormViewModel);
不起作用,但 UpdateModel(dinner);
起作用。
这完全基于为输入元素生成的 name
以及它如何决定分配到视图模型中。我经常在浏览器中检查生成的 HTML 以在 POST.
您可以使用 DropDownListFor
,这样您就可以指定 model.Dinner.Country
,这样它将生成完全符合嵌套关系的 name
。
所以现在你明白为什么它没有按照你想要的方式工作了。从这里开始,如何更实际地解决它有许多我见过的常用变体,但大多数都会涉及视图模型到实体的某种映射,并且通常在另一层,如业务层、数据访问层,或利用 AutoMapper 等工具。
您可以将国家 属性 添加到 ViewModel 的根目录。
或者我希望让您的视图模型更平坦,其中属性未嵌套在 Dinner
中,然后 posts,将您的视图模型映射到您的实体。看起来 Dinner
是您的实体。我从不在我的视图模型中包含我的实体,将实体置于控制器和视图之外是一种很常见的做法。相反,在 GET 期间的某个时刻有一个 Entity->ViewModel 赋值,然后在 post 上有一个 ViewModel->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
});
当然我编了一些属性因为我不知道你的实体长什么样
这允许您重构您的数据库关系,您只需要更改您的映射。