我正在使用ASP.NET MVC2 RC并且无法弄清楚如何获取HTML帮助器 TextBoxfor 以使用 ViewModel模式。在编辑页面上使用时,在控制器中调用UpdateModel()时,不会保存数据。我从NerdDinner应用程序中获取了以下代码示例。
Edit.aspx
<%@ Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.DinnerFormViewModel>" %>
...
<p>
// This works when saving in controller (MVC 1)
<label for="Title">Dinner Title:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
// This does not work when saving in the controller (MVC 2)
<label for="Title">Dinner Title:</label>
<%= Html.TextBoxFor(model => model.Dinner.Title) %>
<%= Html.ValidationMessageFor(model=> model.Dinner.Title) %>
</p>
DinnerController
// POST: /Dinners/Edit/5
[HttpPost, Authorize]
public ActionResult Edit(int id, FormCollection collection) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (!dinner.IsHostedBy(User.Identity.Name))
return View("InvalidOwner");
try {
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
ModelState.AddModelErrors(dinner.GetRuleViolations());
return View(new DinnerFormViewModel(dinner));
}
}
当使用原始帮助器样式(Http.TextBox)时,UpdateModel(晚餐)调用按预期工作,并保存新值。
当使用新的(MVC2)帮助器样式(Http.TextBoxFor)时,UpdateModel(晚餐)调用不会更新值。是的,当前值会在加载时加载到编辑页面中。
我需要在控制器代码中添加其他内容才能使其正常工作吗?如果我只使用模型而不是ViewModel模式,那么新的帮助程序可以正常工作。
谢谢。
答案 0 :(得分:19)
这里的问题是你的编辑表单是对DinnerFormViewModel类型使用强类型助手,但是你在Dinner类型上调用UpdateModel。
当您对类型使用强类型帮助程序时,帮助程序会创建表单字段,假设您要发布到的类型。当类型不匹配时,就会出现问题。
但是,这很容易修复。你可以为UpdateModel提供一个前缀,表示你没有尝试编辑整个模型,你试图编辑模型的属性,在这种情况下是晚餐。
UpdateModel(dinner, "Dinner");
另一种方法是在实际的ViewModel上调用UpdateModel。
var viewModel = new DinnerFormViewModel();
viewModel.Dinner = repository.GetDinner(id);
UpdateModel(viewModel);
我认为第一种方法要好得多。
答案 1 :(得分:2)
在“ Wrox Professional ASP.NET MVC 2 ”一书的第90页上,代码列为:
if (TryUpdateModel(dinner)) {
dinnerRepository.Save();
redirectToAction("Details", new { id=dinner.DinnerID });
但它应该是:
if (TryUpdateModel(dinner, "Dinner")) {
dinnerRepository.Save();
redirectToAction("Details", new { id=dinner.DinnerID });
此方法重载将尝试使用控制器的值提供程序中的值更新指定的模型[Dinner],而不是默认的[ViewModel]。基本上它所做的就是在提供程序中查找它们时为所有值添加前缀。
因此,当模型正在寻找更新其'Title属性时,它将查找Dinner.Title,而不仅仅是控制器值提供程序中的Title。
在调试时,查看Edit ActionResult方法并检查FormCollection输入参数。当你深入了解它的入口数组时,你会发现所有的键都以你在View中引用的属性对象的前缀开头,在你的情况下是编辑视图,如下所示:
<%: Html.TextBoxFor(model => model.Dinner.Title, new {size=50, @class="prettyForm" })%>
答案 2 :(得分:1)
我不是百分百肯定,但似乎强类型帮助器创建了ids /名称“Dinner.Title”而不仅仅是“Title”,因此 - UpdateModel
无法绑定它。
不幸的是 - 我自己没有使用UpdateModel
方法,所以我不知道解决方案。
你能添加为这两种方法呈现的html吗?
玩反射器ATM。
这是我发现的:
protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IValueProvider valueProvider) where TModel: class
{
if (model == null)
{
throw new ArgumentNullException("model");
}
if (valueProvider == null)
{
throw new ArgumentNullException("valueProvider");
}
Predicate<string> predicate = delegate (string propertyName) {
return BindAttribute.IsPropertyAllowed(propertyName, base.includeProperties, base.excludeProperties);
};
IModelBinder binder = this.Binders.GetBinder(typeof(TModel));
ModelBindingContext context2 = new ModelBindingContext();
context2.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(delegate {
return base.model;
}, typeof(TModel));
context2.ModelName = prefix;
context2.ModelState = this.ModelState;
context2.PropertyFilter = predicate;
context2.ValueProvider = valueProvider;
ModelBindingContext bindingContext = context2;
binder.BindModel(base.ControllerContext, bindingContext);
return this.ModelState.IsValid;
}
参数
- model要更新的模型实例 - prefix在值提供程序中查找值时使用的前缀。
所以 - 您可以尝试使用UpdateModel<T>(T model, string prefix)
重载并传递“晚餐”或“晚餐”。作为前缀参数。
答案 3 :(得分:1)
可能更简单的方法如下。如果您正在剪切和粘贴NerDDinner教程的wrox下载代码,您会发现存在一些错误。使用上面的建议,我修改了1-53.txt中的示例以使其工作。改变如下:
//
// POST: /Dinners/Edit/2
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
// Retrieve existing dinner
Dinner dinner = dinnerRepository.GetDinner(id);
DinnerFormViewModel viewModel = new DinnerFormViewModel(dinner);
if (TryUpdateModel(viewModel))
{
// Persist changes back to database
dinnerRepository.Save();
// Perform HTTP redirect to details page for the saved Dinner
return RedirectToAction("Details", new { id = dinner.DinnerID });
}
else
{
return View(viewModel);
}
}
答案 4 :(得分:0)
更简单的方法是使用前缀作为参数名称,就像这样:
public ActionResult Edit(Dinner Dinner, int DinnerID)
{
...
}