asp.net-mvc – 使用模型对象更新ModelState

前端之家收集整理的这篇文章主要介绍了asp.net-mvc – 使用模型对象更新ModelState前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
问题:如何在发布验证场景时更新ModelState.

我有一个简单的形式:

  1. <%= Html.ValidationSummary() %>
  2. <% using(Html.BeginForm())%>
  3. <%{ %>
  4. <%=Html.TextBox("m.Value") %>
  5. <input type="submit" />
  6. <%} %>

用户提交我想要验证输入,在某些情况下,我想修复用户错误,让他知道他做了一个已经修复的错误

  1. [AcceptVerbs(HttpVerbs.Post)]
  2. public ActionResult Index(M m)
  3. {
  4. if (m.Value != "a")
  5. {
  6. ModelState.AddModelError("m.Value","should be \"a\"");
  7. m.Value = "a";
  8. return View(m);
  9. }
  10. return View("About");
  11. }

那么问题是,MVC会简单地忽略传递给视图的模型,并且会重新渲染用户键入的内容,而不是我的值(“a”).
这是因为TextBox渲染器检查是否有ModelState,如果它不为null – 则使用ModelState的值.该值当然是在发布之前键入的一个用户.

由于我无法更改TextBox渲染器的行为,我发现唯一的解决方案是自己更新ModelState.快速的方式是(ab)使用DefaultModelBinder,并通过简单地更改分配方向来覆盖从表单到模型分配值的方法;).使用DefaultModelBinder我不必解析ids.
以下代码(基于DefaultModelBinder的原始实现)是我的解决方案:

  1. /// <summary>
  2. /// Updates ModelState using values from <paramref name="order"/>
  3. /// </summary>
  4. /// <param name="order">Source</param>
  5. /// <param name="prefix">Prefix used by Binder. Argument name in Action (if not explicitly specified).</param>
  6. protected void UpdateModelState(object model,string prefix)
  7. {
  8. new ReversedBinder().BindModel(this.ControllerContext,new ModelBindingContext()
  9. {
  10. Model = model,ModelName = prefix,ModelState = ModelState,ModelType = model.GetType(),ValueProvider = ValueProvider
  11. });
  12. }
  13.  
  14. private class ReversedBinder : DefaultModelBinder
  15. {
  16. protected override void BindProperty(ControllerContext controllerContext,ModelBindingContext bindingContext,System.ComponentModel.PropertyDescriptor propertyDescriptor)
  17. {
  18. string prefix = CreateSubPropertyName(bindingContext.ModelName,propertyDescriptor.Name);
  19. object val = typeof(Controller)
  20. .Assembly.GetType("System.Web.Mvc.DictionaryHelpers")
  21. .GetMethod("DoesAnyKeyHavePrefix")
  22. .MakeGenericMethod(typeof(ValueProviderResult))
  23. .Invoke(null,new object[] { bindingContext.ValueProvider,prefix });
  24. bool res = (bool)val;
  25. if (res)
  26. {
  27.  
  28. IModelBinder binder = new ReversedBinder();//this.Binders.GetBinder(propertyDescriptor.PropertyType);
  29. object obj2 = propertyDescriptor.GetValue(bindingContext.Model);
  30.  
  31. ModelBindingContext context2 = new ModelBindingContext();
  32. context2.Model = obj2;
  33. context2.ModelName = prefix;
  34. context2.ModelState = bindingContext.ModelState;
  35. context2.ModelType = propertyDescriptor.PropertyType;
  36. context2.ValueProvider = bindingContext.ValueProvider;
  37. ModelBindingContext context = context2;
  38. object obj3 = binder.BindModel(controllerContext,context);
  39.  
  40. if (bindingContext.ModelState.Keys.Contains<string>(prefix))
  41. {
  42. var prefixKey = bindingContext.ModelState.Keys.First<string>(x => x == prefix);
  43. bindingContext.ModelState[prefixKey].Value
  44. = new ValueProviderResult(obj2,obj2.ToString(),bindingContext.ModelState[prefixKey].Value.Culture);
  45. }
  46. }
  47. }
  48. }

所以问题依然存在:我在做一些非常不寻常的事情,还是我错过了什么?如果是前者,那么如何以更好的方式实现这些功能(使用现有的MVC基础架构)?

解决方法

我知道这篇文章是相当古老的,但这是我以前遇到过的一个问题,我只是想到了一个我喜欢的简单解决方案 – 只要清除ModelState后,你可以看到已发布的值.
  1. UpdateModel(viewmodel);
  2. ModelState.Clear();
  3.  
  4. viewmodel.SomeProperty = "a new value";
  5. return View(viewmodel);

并且视图必须使用(可能修改的)视图模型对象而不是ModelState.

也许这真的很明显.事后看起来好像很好

猜你在找的asp.Net相关文章