2010-01-29 4 views
18

Ich arbeite mit ASP.NET MVC2 RC und kann nicht herausfinden, wie die HTML-Helfer zu bekommen, TextBoxfor mit einemViewmodel Muster zu arbeiten. Bei Verwendung auf einer Bearbeitungsseite werden die Daten nicht gespeichert, wenn UpdateModel() im Controller aufgerufen wird. Ich habe die folgenden Codebeispiele aus der NerdDinner-Anwendung genommen.Mit Ansichtsmodell Muster mit MVC 2 stark typisierten HTML Helfer

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)); 
    } 
} 

Wenn der ursprüngliche Helfer Stil verwendet wird (Http.TextBox) die Updatemodel (Abendessen) Aufruf funktioniert wie erwartet und die neuen Werte werden gespeichert.

Wenn der neue (MVC2) Helfer Stil verwendet wird (Http.TextBoxFor) das Updatemodel (Abendessen) -Aufruf die Werte nicht aktualisiert. Ja, die aktuellen Werte werden beim Laden in die Bearbeitungsseite geladen.

Gibt es noch etwas, das ich dem Controller-Code hinzufügen muss, damit es funktioniert? Der neue Helfer funktioniert gut, wenn ich nur ein Modell und kein ViewModel-Muster verwende.

Vielen Dank.

+0

Hallo, ich habe Das gleiche Problem beim Erstellen von Maßnahmen. Können Sie bitte einen Blick http://stackoverflow.com/questions/2494940/custom-viewmodel-with-mvc-2-strongly-typed-html-helpers-return-null-object-on-cre –

Antwort

19

Das Problem hier ist Ihr Edit-Formular stark typisierten Helfer gegen einen DinnerFormViewModel Typen verwendet, aber Sie anrufen Update auf einem Dinner-Typ.

Wenn Sie die stark typisierte Helfer gegen die Art zu verwenden, erstellen die Helfer Formularfelder unter der Annahme, das ist der Typ, den Sie zu veröffentlichen sind. Wenn die Typen nicht übereinstimmen, liegt ein Problem vor.

Dies ist jedoch sehr einfach zu beheben. Sie können UpdateModel ein Präfix geben, das angibt, dass Sie nicht versucht haben, das gesamte Modell zu bearbeiten. Sie haben versucht, eine Eigenschaft des Modells zu bearbeiten, in diesem Fall ein Abendessen.

UpdateModel(dinner, "Dinner"); 

Der andere Ansatz besteht darin, UpdateModel auf dem tatsächlichen ViewModel aufzurufen.

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

Ich denke, der erste Ansatz ist viel besser.

+1

Es gibt nichts Besseres, als Ihre Antwort vom PM eines Projekts zu erhalten. Danke Phil, das hat super funktioniert. Ich habe dein erstes Beispiel benutzt, das war einfach und geradlinig wie ich gehofft hatte. – Brettski

+0

Ich folge dem gleichen Beispiel, aber Ausnahme beim Aktualisieren des Modells: http://StackOverflow.com/Questions/2377065/How-to-Update-using-mvc2-rc2 – Picflight

+0

Hallo Phil, ich habe das gleiche Problem mit Aktion erstellen . Ich verwende ein benutzerdefiniertes Ansichtsmodell für das Erstellungsformular. Wenn ich versuche, ein Objekt zu erstellen, wird es nicht aus dem Formular gebunden. Also habe ich nach der Quelle gesucht und "Category.Title" statt "Title" gefunden. Wie kann ich es reparieren. Vielen Dank –

1

Ich bin nicht 100% sicher, aber es scheint, dass stark typisierte Helfer IDs/Namen "Dinner.Title" statt nur "Titel" erstellt und daher - UpdateModel kann nicht binden.

Leider - ich habe nicht verwendet UpdateModel Methode selbst ich weiß also nicht die Lösung.

Könnten Sie HTML hinzufügen, das für beide Ansätze gerendert wird?


Herumspielen mit Reflektor ATM.

Das ist, was ich gefunden:

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; 
} 

Parameter
- Modell Das Modell Instanz zu aktualisieren.
- Präfix Das Präfix, das beim Suchen nach Werten im Wertanbieter verwendet wird.


So - können Sie versuchen, UpdateModel<T>(T model, string prefix) Überlastung zu verwenden und "Abendessen" oder passieren "Dinner." als Präfix-Argument.

+0

Danke, das scheint den Trick zu machen. Das Problem, das ich habe, ist, warum fügen Sie die stark typisierten Helfer zum Gerüst hinzu, wenn Sie durch einen Reifen springen müssen, um sie zu verwenden. Ich muss hier etwas vermissen. – Brettski

+0

@Brettski Ich hatte auch einige Schwierigkeiten mit ihnen (http://stackoverflow.com/questions/2093216/asp-net-mvc2-strongly-typed-htmlhelper-indexes). Du verpasst nichts - sie sind einfach nicht reif genug, das ist alles. Zumindest - so sehe ich es. –

0

Eine einfachere Art und Weise wird mit dem Präfix als Parameternamen, tun wie folgt aus:

public ActionResult Edit(Dinner Dinner, int DinnerID) 
{ 
    ... 
} 
1

Vielleicht ein einfacher Weg, dies zu setzen ist wie folgt. Wenn Sie Code aus dem Wrox-Download für das NerDDinner-Tutorial ausschneiden und einfügen, werden Sie feststellen, dass einige Fehler vorliegen. Unter Verwendung eines Vorschlags von oben modifizierte ich das Beispiel von 1-53.txt, um dies zum Laufen zu bringen. Die Änderung folgt:

// 
    // 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

Auf 90 Seite im "Wrox Professionelle ASP.NET MVC 2" Buch wird der Code aufgeführt als:

if (TryUpdateModel(dinner)) { 
    dinnerRepository.Save(); 

    redirectToAction("Details", new { id=dinner.DinnerID }); 

Aber es sollte lauten:

if (TryUpdateModel(dinner, "Dinner")) { 
    dinnerRepository.Save(); 

    redirectToAction("Details", new { id=dinner.DinnerID }); 

Bei dieser Methodenüberladung wird versucht, das angegebene Modell [Abendessen] anstelle des Standardwerts [ViewModel] unter Verwendung der Werte fr zu aktualisieren om Wertgeber des Controllers. Alles was es tut, ist ein Präfix zu allen Ihren Werten hinzuzufügen, wenn Sie im Anbieter nachsehen.

Wenn also das Modell seine Title-Eigenschaft aktualisieren will, sucht es nach Dinner.Title und nicht nur nach Title im Value Provider des Controllers.

Überprüfen Sie beim Debugging die ActionResult-Methode bearbeiten und überprüfen Sie den FormCollection-Eingabeparameter. Wenn Sie graben in sie Eintrag Array ist, werden Sie Schlüssel finden, die alle mit dem Präfix des Immobilienobjekts starten Sie in Ihrer View verwiesen, die Bearbeiten-Ansicht in Ihrem Fall wie folgt aus:

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