2010-01-29 61 views
18

我与ASP.NET MVC2 RC工作,无法弄清楚如何获得HTML帮助,TextBoxfor视图模型模式工作。在编辑页面上使用时,在控制器中调用UpdateModel()时不会保存数据。我从NerdDinner应用程序中获得了以下代码示例。使用视图模型模式与MVC 2强类型HTML辅助

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(dinner)调用不会更新这些值。是的,当前值在加载时加载到编辑页面。

是否还有其他东西需要添加到控制器代码中才能使用?如果我只是使用模型而不是ViewModel模式,那么新的帮助器可以正常工作。

谢谢。

+0

嗨,我有关于创建操作的相同问题。你可以请看一下 http://stackoverflow.com/questions/2494940/custom-viewmodel-with-mvc-2-strongly-typed-html-helpers-return-null-object-on-cre – 2010-03-23 12:02:43

回答

19

这里的问题是你的Edit表单对DinnerFormViewModel类型使用强类型助手,但是你在Dinner类型上调用UpdateModel。

当您针对类型使用强类型助手时,助手会创建表单字段,前提是您要发布的类型。当类型不匹配时,就会出现问题。

但是,这很容易解决。您可以为UpdateModel提供一个前缀,表示您并未尝试编辑整个模型,而是尝试编辑模型的属性,在本例中为Dinner。

UpdateModel(dinner, "Dinner"); 

另一种方法是在实际的ViewModel上调用UpdateModel。

var viewModel = new DinnerFormViewModel(); 
viewModel.Dinner = repository.GetDinner(id); 
UpdateModel(viewModel); 

我认为第一种方法好得多。

+1

没有什么比从一个项目的PM获得答案更好的了。谢谢你,菲尔,这很好。我用你的第一个例子,这是一个简单而直接的例子,正如我所希望的那样。 – Brettski 2010-01-31 21:03:59

+0

我正在按照相同的例子,但更新模型异常:http://stackoverflow.com/questions/2377065/how-to-update-using-mvc2-rc2 – Picflight 2010-03-06 08:54:07

+0

嗨,菲尔,我有同样的问题与创建操作。我为创建表单使用自定义视图模型。当我尝试创建一个对象时,它不会从窗体绑定。所以我看了看源代码,发现“Category.Title”而不是“Title”。我如何解决它。谢谢 – 2010-03-22 18:36:34

1

我不是100%确定,但它似乎强类型助手创建ids /名称“Dinner.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; 
} 

参数
- 模型的模型实例进行更新。
- 前缀在值提供程序中查找值时使用的前缀。


所以 - 你可以尝试使用UpdateModel<T>(T model, string prefix)过载,并通过“晚餐”或“晚餐”。作为前缀参数。

+0

谢谢,这看起来确实有用。我遇到的问题是,为什么要将强类型助手添加到脚手架,如果你需要跳过箍来使用它们。我必须在这里失去一些东西。 – Brettski 2010-01-29 15:04:04

+0

@Brettski我也遇到过一些困难(http://stackoverflow.com/questions/2093216/asp-net-mvc2-strongly-typed-htmlhelper-indexes)。你不会错过任何东西 - 它们还不够成熟,就这些。至少 - 我就是这么看的。 – 2010-01-29 15:08:09

0

一个更简单的方法是使用前缀作为参数的名字,只是这样做:

public ActionResult Edit(Dinner Dinner, int DinnerID) 
{ 
    ... 
} 
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); 
    } 
    } 
2

在第90页中Wrox的专业ASP.NET MVC 2书的代码被列为:

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],使用值fr om控制器的价值提供者。基本上它所做的只是在提供者中查找它们时为所有值添加前缀。

因此,当模型正在寻找更新其'Title属性时,它将查找Dinner.Title,而不是控制器值提供程序中的Title。

在调试时,请查看Edit ActionResult方法并检查FormCollection输入参数。当你挖成它的入口阵列,你会发现按键,所有的开始你在视图中引用的属性对象的前缀,你的情况,编辑,查看,就像这样:

<%: Html.TextBoxFor(model => model.Dinner.Title, new {size=50, @class="prettyForm" })%>