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

时间:2010-02-25 10:40:11

标签: 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>

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

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

    <fieldset>
        <legend>Fields</legend>

        <%=Html.EditorForModel() %>

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

<% } %>

在我的编辑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;
    }
}

控制器设置如下:

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似乎很接近,但没有帮助。

3 个答案:

答案 0 :(得分: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>
<% } %>



其次,您需要更改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;
    }
}

到目前为止,您可能会说: Dude,我的 [ProductId] 属性在哪里?“。
简答:你不需要它!

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

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

这是您的HTTP POST操作方法,它接受2个参数 “id”来自&lt; form&gt;标签的动作属性。

[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(重定向后获取)模式。

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 );

这有助于我使从回发数据中绑定模型对象的数据变得简单。 此外,它使它更安全,因为您只绑定要绑定的数据。没什么,没什么。

如果您有任何疑问,请与我联系。

答案 1 :(得分:1)

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

答案 2 :(得分:0)

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

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