【问题标题】:ViewModel with SelectList binding in ASP.NET MVC2在 ASP.NET MVC2 中具有 SelectList 绑定的 ViewModel
【发布时间】:2010-06-19 07:10:56
【问题描述】:

我正在尝试为名为 Product 的 Linq2SQL 实体实现一个 Edit ViewModel。它有一个链接到品牌列表的外键。

目前我正在通过 ViewData 填充品牌列表并使用 DropDownListFor,因此:

<div class="editor-field">
    <%= Html.DropDownListFor(model => model.BrandId, (SelectList)ViewData["Brands"])%>
    <%= Html.ValidationMessageFor(model => model.BrandId) %>
</div>

现在我想重构视图以使用强类型 ViewModel 和 Html.EditorForModel():

<% using (Html.BeginForm()) {%>
    <%= Html.ValidationSummary(true) %>

    <fieldset>
        <legend>Fields</legend>

        <%=Html.EditorForModel() %>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

<% } %>

在我的编辑视图模型中,我有以下内容:

public class EditProductViewModel
{
    [HiddenInput]
    public int ProductId { get; set; }

    [Required()]
    [StringLength(200)]
    public string Name { get; set; }

    [Required()]
    [DataType(DataType.Html)]
    public string Description { get; set; }

    public IEnumerable<SelectListItem> Brands { get; set; }

    public int BrandId { get; set; }

    public EditProductViewModel(Product product, IEnumerable<SelectListItem> brands)
    {
        this.ProductId = product.ProductId;
        this.Name = product.Name;
        this.Description = product.Description;
        this.Brands = brands;
        this.BrandId = product.BrandId;
    }
}

控制器设置如下:

public ActionResult Edit(int id)
{
    BrandRepository br = new BrandRepository();

    Product p = _ProductRepository.Get(id);
    IEnumerable<SelectListItem> brands = br.GetAll().ToList().ToSelectListItems(p.BrandId);

    EditProductViewModel model = new EditProductViewModel(p, brands);

    return View("Edit", model);
}

ProductId、Name 和 Description 在生成的视图中正确显示,但选择列表没有。品牌列表肯定包含数据。

如果我在视图中执行以下操作,则 SelectList 是可见的:

<% using (Html.BeginForm()) {%>
    <%= Html.ValidationSummary(true) %>

    <fieldset>
        <legend>Fields</legend>

        <%=Html.EditorForModel() %>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.BrandId) %>
        </div>
        <div class="editor-field">
            <%= Html.DropDownListFor(model => model.BrandId, Model.Brands)%>
            <%= Html.ValidationMessageFor(model => model.BrandId) %>
        </div>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>

<% } %>

我做错了什么? EditorForModel() 一般不支持 SelectList 吗?我是否缺少某种 DataAnnotation?

我似乎在 ViewModels 中找不到任何有用的 SelectList 用法示例。我真的很难过。 This answer 似乎很接近,但没有帮助。

【问题讨论】:

标签: asp.net-mvc mvvm


【解决方案1】:

君托,

Html.EditorForModel() 方法不够智能,无法将BrandIdBrands 选择列表匹配。

首先,您不能使用快捷方式EditorForModel() 方法。
您必须像这样创建自己的 HTML 模板。

<% using (Html.BeginForm()) { %>

    <div style="display:none"><%= Html.AntiForgeryToken() %></div>

    <table>
        <tr>
            <td><%= Html.LabelFor(m => m.Name) %></td>
            <td><%= Html.EditorFor(m => m.Name) %></td>
        </tr>

        <tr>
            <td><%= Html.LabelFor(m => m.Description) %></td>
            <td><%= Html.EditorFor(m => m.Description) %></td>
        </tr>

        <tr>
            <td><%= Html.LabelFor(m => m.BrandId) %></td>
            <td><%= Html.EditorFor(m => m.BrandId) %></td>
        </tr>
    </table>
<% } %>



其次,你需要改变你的 Action 方法。

[ImportModelStateFromTempData]
public ActionResult Edit(int id)
{
    BrandRepository br = new BrandRepository();

    Product p = _ProductRepository.Get(id);
    ViewData["BrandId"] = br.GetAll().ToList().ToSelectListItems(p.BrandId);

    EditProductViewModel model = new EditProductViewModel(p);

    return View("Edit", model);
}



第三,你需要更新你的EditProductViewModel 类。

public class EditProductViewModel
{
    [Required]
    [StringLength(200)]
    public string Name { get; set; }

    [Required()]
    [DataType(DataType.Html)]
    public string Description { get; set; }

    [Required] // this foreign key *should* be required
    public int BrandId { get; set; }

    public EditProductViewModel(Product product)
    {
        this.Name = product.Name;
        this.Description = product.Description;
        this.BrandId = product.BrandId;
    }
}

现在,您可能会说:老兄,我的 [ProductId] 属性在哪里?”。
简短的回答:你不需要它!

您的视图呈现的 HTML 已经指向带有适当“ProductId”的“编辑”操作方法,如下所示。

<form action="/Product/Edit/123" method="post">
    ...
</form>

这是您的 HTTP POST 操作方法,它接受 2 个参数。
“id”来自

标签的 action 属性。
[HttpPost, ValidateAntiForgeryToken, ExportModelStateToTempData]
public ActionResult Edit(int id, EditProductViewModel model)
{
    Product p = _ProductRepository.Get(id);

    // make sure the product exists
    // otherwise **redirect** to [NotFound] view because this is a HTTP POST method
    if (p == null)
        return RedirectToAction("NotFound", new { id = id });

    if (ModelState.IsValid)
    {
        TryUpdateModel<Product>(p);
        _ProductRepository.UpdateProduct( p );
    }

    return RedirectToAction("Edit", new { id = id });
}

ExportModelStateToTempDataImportModelStateFromTempData 非常有用。
这些属性用于 PRG(Post Redirect Get)模式。

阅读 Kazi Manzur Ra​​shid 的这篇博文中的使用 PRG 模式进行数据修改部分。
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx




好吧,这个数据绑定代码不是我最喜欢的做事方式。

TryUpdateModel<Product>( p );

我最喜欢的做法是有一个单独的 interface 用于纯数据绑定。

public interface IProductModel
{
    public string Name {get; set;}
    public string Description {get; set;}
    public int BrandId {get; set;}
}

public partial class Product : IProductModel
{
}

public partial class EditProductViewModel : IProductModel
{
}

这就是我更新数据绑定代码的方式。

TryUpdateModel<IProductModel>( p );

这有助于我轻松地从回发数据中绑定我的模型对象。 此外,它使其更安全,因为您只绑定要绑定的数据。不多也不少。

如果您有任何问题,请告诉我。

【讨论】:

  • 谢谢眩晕。迄今为止最好的答案。总之,EdutorForModel() 不够聪明,我仍然必须使用 ViewData 和魔术字符串来获取 SelectList。非常有用的攻略。谢谢。
【解决方案2】:

您的属性 BrandId 的新属性 DropDownList 可能会有所帮助。查看Extending ASP.NET MVC 2 Templates 文章。但是这种方法使用 ViewData 作为选择列表的项目源。

【讨论】:

    【解决方案3】:

    您应该将该查找构建到您的 ViewModel 中。然后创建一个构建 ViewModel 并填充该查找的 Builder 对象。

    毕竟,这就是您的 ViewModel 的用途:专门为您的视图提供模型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-12-15
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-29
      相关资源
      最近更新 更多