我正在尝试为名为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似乎很接近,但没有帮助。
答案 0 :(得分:4)
帮派,
Html.EditorForModel()
方法不够智能,无法将BrandId
与Brands
选择列表匹配。
首先,您无法使用快捷方式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 });
}
ExportModelStateToTempData
和ImportModelStateFromTempData
非常有用
这些属性用于PRG(重定向后获取)模式。
Kazi Manzur Rashid 在此博文中阅读使用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的用途:专门为您的视图提供模型。