Das Problem: Aktualisieren von ModelState im Buchungs + Validierungsszenario.Aktualisieren von ModelState mit Modellobjekt
Ich habe eine einfache Form bekommt:
<%= Html.ValidationSummary() %>
<% using(Html.BeginForm())%>
<%{ %>
<%=Html.TextBox("m.Value") %>
<input type="submit" />
<%} %>
Wenn Benutzer senden I-Eingang überprüfen möge und in einigen Fällen mag ich den Fehler für den Benutzer zu beheben, um ihn wissen zu lassen, dass er einen Fehler gemacht, das ist bereits fest:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(M m)
{
if (m.Value != "a")
{
ModelState.AddModelError("m.Value", "should be \"a\"");
m.Value = "a";
return View(m);
}
return View("About");
}
Nun das Problem ist, wird MVC einfach das Modell zu der Ansicht geführt ignorieren und wieder machen, was der Benutzer eingegeben - und nicht meinen Wert („a“). Dies passiert, weil der TextBox-Renderer prüft, ob ein ModelState vorhanden ist und ob er nicht null ist - der Wert von ModelState wird verwendet. Dieser Wert ist natürlich derjenige, der vor dem Posten eingegeben wurde.
Da ich das Verhalten des TextBox-Renderers nicht ändern kann, wäre die einzige Lösung, die ich gefunden habe, die Aktualisierung des ModelState durch mich selbst. Der Quick'n'dirty-Weg besteht darin, den DefaultModelBinder (ab) zu verwenden und die Methode zu überschreiben, die die Werte von Formularen dem Modell zuweist, indem einfach die Zuweisungsrichtung geändert wird;). Mit DefaultModelBinder muss ich die IDs nicht analysieren. Der folgende Code (basierend auf Original Implementierung von Default) ist meine Lösung für dieses Problem:
/// <summary>
/// Updates ModelState using values from <paramref name="order"/>
/// </summary>
/// <param name="order">Source</param>
/// <param name="prefix">Prefix used by Binder. Argument name in Action (if not explicitly specified).</param>
protected void UpdateModelState(object model, string prefix)
{
new ReversedBinder().BindModel(this.ControllerContext,
new ModelBindingContext()
{
Model = model,
ModelName = prefix,
ModelState = ModelState,
ModelType = model.GetType(),
ValueProvider = ValueProvider
});
}
private class ReversedBinder : DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
string prefix = CreateSubPropertyName(bindingContext.ModelName, propertyDescriptor.Name);
object val = typeof(Controller)
.Assembly.GetType("System.Web.Mvc.DictionaryHelpers")
.GetMethod("DoesAnyKeyHavePrefix")
.MakeGenericMethod(typeof(ValueProviderResult))
.Invoke(null, new object[] { bindingContext.ValueProvider, prefix });
bool res = (bool)val;
if (res)
{
IModelBinder binder = new ReversedBinder();//this.Binders.GetBinder(propertyDescriptor.PropertyType);
object obj2 = propertyDescriptor.GetValue(bindingContext.Model);
ModelBindingContext context2 = new ModelBindingContext();
context2.Model = obj2;
context2.ModelName = prefix;
context2.ModelState = bindingContext.ModelState;
context2.ModelType = propertyDescriptor.PropertyType;
context2.ValueProvider = bindingContext.ValueProvider;
ModelBindingContext context = context2;
object obj3 = binder.BindModel(controllerContext, context);
if (bindingContext.ModelState.Keys.Contains<string>(prefix))
{
var prefixKey = bindingContext.ModelState.Keys.First<string>(x => x == prefix);
bindingContext.ModelState[prefixKey].Value
= new ValueProviderResult(obj2, obj2.ToString(),
bindingContext.ModelState[prefixKey].Value.Culture);
}
}
}
}
So bleibt die Frage: bin ich etwas extrem selten tun oder bin ich etwas fehlt? Wenn Ersteres, wie könnte ich solche Funktionalität besser implementieren (unter Verwendung der bestehenden MVC-Infrastruktur)?
Aber ich möchte verbindlich den Standard erhalten. Ich möchte es, weil ich ModelState verwenden möchte. Ich möchte nur den ModelState aktualisieren, um Änderungen in meinem Modellobjekt widerzuspiegeln. – user87338
Bitte sehen Sie meine Bearbeitung. –
Ihr Kommentar ist genau das, was ich tue, aber Sie tun es selbst, und ich benutze Te-Mappe, so dass ich etwas mehr "generische" habe. Die Änderung des Wertes ("a") passiert in einer niedrigeren Reihe, also weiß ich nicht wirklich, welche Stützen sich geändert haben. Und auch Sie möchten nicht ModelState ["m.Value"]. Wert = neu ValueProviderResult ("a", m.Name, CultureInfo.CurrentCulture); für jedes Objekt Eigenschaft, tun Sie :). – user87338