在ASP.NET MVC2中使用SelectList绑定的ViewModel

Reb*_*cca 8 asp.net-mvc mvvm

我正在尝试为名为Product的Linq2SQL实体实现Edit ViewModel.它有一个与品牌列表相关联的外键.

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

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

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

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

    <fieldset>
        <legend>Fields</legend>

        <%=Html.EditorForModel() %>

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

<% } %>
Run Code Online (Sandbox Code Playgroud)

在我的编辑ViewModel中,我有以下内容:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

控制器设置如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

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>

<% } %>
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?EditorForModel()一般不支持SelectList吗?我错过了某种DataAnnotation吗?

我似乎无法在ViewModels中找到任何有用的SelectList用法示例.我真的很难过.这个答案似乎很接近,但没有帮助.

stu*_*tun 4

帮派,

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>
<% } %>
Run Code Online (Sandbox Code Playgroud)



其次,您需要更改您的 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);
}
Run Code Online (Sandbox Code Playgroud)



第三,你需要更新你的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

<form action="/Product/Edit/123" method="post">
    ...
</form>
Run Code Online (Sandbox Code Playgroud)

这是您的 HTTP POST 操作方法,它接受 2 个参数。
“id”来自 <form> 标记的 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 });
}
Run Code Online (Sandbox Code Playgroud)

和非常有用ExportModelStateToTempData。 这些属性用于 PRG(重定向后获取)模式。ImportModelStateFromTempData

请阅读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 );
Run Code Online (Sandbox Code Playgroud)

我最喜欢的方法是有一个单独的 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
{
}
Run Code Online (Sandbox Code Playgroud)

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

TryUpdateModel<IProductModel>( p );
Run Code Online (Sandbox Code Playgroud)

这有帮助的是,它使我可以轻松地从回发数据中对模型对象进行数据绑定。此外,它还使其更加安全,因为您只绑定想要绑定的数据。不多不少。

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