ViewModel 中未填写下拉值

Pulldown values not filled in in ViewModel

我正在学习 Hanselman 的 MVC 教程,并尝试在创建和编辑视图中使用视图模型来填充下拉菜单。在此我们被指示创建一个 table Dinner,列包括 TitleCountry 以及相关的 类,然后是一个包含 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       
   });

当然我编了一些属性因为我不知道你的实体长什么样

这允许您重构您的数据库关系,您只需要更改您的映射。