2009-07-23 9 views
3

Lasst uns sagen, dass Sie ein Modell haben, die Art wie folgt aussieht:MVC Update wenn die Namen nicht übereinstimmen

public class MyClass { 
    public string Name { get; set; } 
    public DateTime MyDate { get; set; } 
} 

Die Standard-Vorlage bearbeiten, die Visual Studio gibt Ihnen eine einfache Textbox für die MyDate Eigentum. Das ist alles schön und gut, aber lassen Sie uns sagen, dass Sie, dass bis aufspalten müssen in es ist Monat/Tag/Jahr-Komponenten und Ihre Form wie folgt aussieht:

<label for="MyDate">Date:</label> 
<%= Html.TextBox("MyDate-Month", Model.MyDate.Month) %> 
<%= Html.TextBox("MyDate-Day", Model.MyDate.Day) %> 
<%= Html.TextBox("MyDate-Year", Model.MyDate.Year) %> 

Wenn dieses eingereicht, um einen Anruf zu UpdateModel won‘ t arbeiten, da es keine Definition für MyDate-Month gibt. Gibt es eine Möglichkeit, dem Projekt einen benutzerdefinierten Ordner hinzuzufügen, um Situationen wie diese zu behandeln, oder wenn die HTML-Eingaben (aus welchen Gründen auch immer) anders benannt werden?

Eine Abhilfe, die ich gefunden habe, ist JavaScript zu verwenden, um einen versteckten Eingang in die Form vor der Vorlage zu injizieren, die die Felder verkettet und richtig benannt, aber das fühlt sich falsch an.

Antwort

6

Ich würde vorschlagen, dass Sie ein eigenes Modell Binder:

using System; 
using System.Globalization; 
using System.Web.Mvc; 

public class MyClassBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     var model = (MyClass)base.CreateModel(controllerContext, bindingContext, modelType); 

     var day = bindingContext.ValueProvider["MyDate-Day"]; 
     var month = bindingContext.ValueProvider["MyDate-Month"]; 
     var year = bindingContext.ValueProvider["MyDate-Year"]; 

     var dateStr = string.Format("{0}/{1}/{2}", month.AttemptedValue, day.AttemptedValue, year.AttemptedValue); 
     DateTime date; 
     if (DateTime.TryParseExact(dateStr, "MM/dd/yyyy", null, DateTimeStyles.None, out date)) 
     { 
      model.MyDate = date; 
     } 
     else 
     { 
      bindingContext.ModelState.AddModelError("MyDate", "MyDate has invalid format"); 
     } 

     bindingContext.ModelState.SetModelValue("MyDate-Day", day); 
     bindingContext.ModelState.SetModelValue("MyDate-Month", month); 
     bindingContext.ModelState.SetModelValue("MyDate-Year", year); 

     return model; 
    } 
} 

dies zu Ihrer Controller-Aktion vereinfacht:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult MyAction(MyClass myClass) 
{ 
    if (!ModelState.IsValid) 
    { 
     return View(myClass); 
    } 
    // Do something with myClass 
    return RedirectToAction("success"); 
} 

und das Bindemittel in Global.asax registrieren:

protected void Application_Start() 
{ 
    RegisterRoutes(RouteTable.Routes); 
    ModelBinders.Binders.Add(typeof(MyClass), new MyClassBinder()); 
} 
+0

Interessanter Ansatz. Ich kenne mich nicht mit benutzerdefinierten Modellbindern aus. Wie kann ich dies vom Controller aus aufrufen? Und muss ich etwas für "normale" Eigenschaften hinzufügen (wie der Name in meinem Beispiel) oder wird der Standardbinder dafür sorgen? Vielen Dank. – swilliams

+0

In meinem ursprünglichen Beitrag schlug ich vor, IModelBinder direkt zu implementieren, was bedeuten würde, dass Sie alle Eigenschaften manuell behandeln müssen und nicht nur MyDate, was mühsam werden könnte. Ich habe meinen Beitrag so geändert, dass MyClassBinder jetzt vom DefaultModelBinder abgeleitet wird, was bedeutet, dass Standardeigenschaften wie "Name" als Standard behandelt werden. –

+0

Sie müssen es nicht vom Controller aus aufrufen. Sie müssen nur den Binder in Application_Start registrieren, und er wird vom Framework vor der Controller-Aktion genauso wie der Standardmodellbinder aufgerufen. –

4

Ein einfacher Weg, dies zu handhaben wäre die Werte aus dem Valueprovider manuell zu erhalten und das Datum Server-Seite zu konstruieren, mit einer weißen Liste mit Update, das diese Eigenschaften ausschließt.

int month = int.Parse(this.ValueProvider["MyDate-Month"].AttemptedValue); 
    int day = ... 
    int year = ... 

    var model = db.Models.Where(m = > m.ID == id); 
    var whitelist = new string[] { "Name", "Company", ... }; 

    UpdateModel(model, whitelist); 

    model.MyDate = new DateTime(year, month, day); 

Natürlich müssten Sie die Validierung/Fehlerbehandlung auch manuell hinzufügen.

+0

That würde funktionieren. Obwohl ich ValueProvider [] verwenden müsste. AttemptedValue, RawValue gibt ein Array von String zurück :). – swilliams

+0

Ich habe das aktualisiert. – tvanfosson

Verwandte Themen