2010-01-29 15 views
18

sto lavorando con ASP.NET MVC2 RC e non riesco a capire come ottenere l'helper HTML, TextBoxfor di lavorare con un ViewModel modello. Se utilizzato in una pagina di modifica, i dati non vengono salvati quando nel controller viene chiamato UpdateModel(). Ho preso i seguenti esempi di codice dall'applicazione NerdDinner.usando il modello ViewModel con MVC 2 fortemente tipizzato HTML Helpers

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

Quando si utilizza lo stile di supporto originale (Http.TextBox) il (cena) chiamata UpdateModel funziona come previsto e i nuovi valori vengono salvati.

Quando viene utilizzato il nuovo stile di supporto (MVC2) (Http.TextBoxFor), la chiamata UpdateModel (cena) non aggiorna i valori. Sì, i valori correnti vengono caricati nella pagina di modifica al caricamento.

C'è qualcos'altro che devo aggiungere al codice del controller perché funzioni? Il nuovo helper funziona bene se sto usando un modello e non un pattern ViewModel.

Grazie.

+0

Ciao, ho lo stesso problema per creare un'azione. Potete per favore dare un'occhiata a http://stackoverflow.com/questions/2494940/custom-viewmodel-with-mvc-2-strongly-typed-html-helpers-return-null-object-on-cre –

risposta

19

Il problema qui è che il tuo modulo di modifica utilizza helper fortemente tipizzati contro un tipo DinnerFormViewModel, ma stai chiamando UpdateModel su un tipo Dinner.

Quando si utilizzano gli helper fortemente tipizzati rispetto al tipo, gli helper creano i campi modulo assumendo che sia il tipo a cui si sta postando. Quando i tipi non corrispondono, c'è un problema.

Tuttavia, questo è molto facile da risolvere. È possibile fornire un prefisso a UpdateModel che indica che non si stava tentando di modificare l'intero modello, si stava tentando di modificare una proprietà del modello, in questo caso una cena.

UpdateModel(dinner, "Dinner"); 

L'altro approccio è chiamare UpdateModel sul ViewModel effettivo.

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

Penso che il primo approccio sia molto meglio.

+1

Non c'è niente di meglio che ottenere la risposta dal PM di un progetto. Grazie Phil, hai funzionato alla grande. Ho usato il tuo primo esempio, che era semplice e diretto come speravo. – Brettski

+0

Sto seguendo lo stesso esempio ma ottengo eccezione per l'aggiornamento del modello: http://stackoverflow.com/questions/2377065/how-to-update-using-mvc2-rc2 – Picflight

+0

Ciao Phil, ho lo stesso problema con Crea azione . Sto usando un modello di visualizzazione personalizzato per il modulo di creazione. Quando provo a creare un oggetto, non viene vincolato dal modulo. Così ho dato un'occhiata alla fonte e ho scoperto che "Category.Title" invece di "Title". Come posso aggiustarlo Grazie –

1

Non sono sicuro al 100%, ma sembra che l'helper fortemente tipizzato crei id/nomi "Dinner.Title" invece di solo "Titolo" e quindi - UpdateModel non può vincolarlo.

Sfortunatamente - non ho usato il metodo UpdateModel personalmente, quindi non conosco la soluzione.

Potrebbe aggiungere HTML che viene visualizzato per entrambi gli approcci?


Giocare in giro con riflettore ATM.

Questo è quello che ho trovato:

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

Parametri
- Modello L'istanza modello da aggiornare.
- prefisso Il prefisso da utilizzare durante la ricerca dei valori nel provider valori.


Così - si può provare a utilizzare UpdateModel<T>(T model, string prefix) sovraccarico e passare "cena" o "Cena". come argomento prefisso.

+0

Grazie, sembra fare il trucco. Il problema che sto avendo è questo perché aggiungere gli helper fortemente tipizzati all'impalcatura se è necessario saltare attraverso un telaio per usarli. Devo mancare qualcosa qui. – Brettski

+0

@Brettski Ho avuto qualche difficoltà anche con loro (http://stackoverflow.com/questions/2093216/asp-net-mvc2-strongly-typed-htmlhelper-indexes). Non ti manca nulla - non sono abbastanza maturi, tutto qui. Almeno - è così che lo vedo. –

0

Un modo più semplice sta usando il prefisso come nome del parametro, basta fare in questo modo:

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

Forse un modo più semplice per mettere questo è il seguente. Se stai tagliando e incollando il codice dal download wrox per il tutorial di NerDDinner, scoprirai che ci sono alcuni errori. Usando un suggerimento dall'alto, ho modificato l'esempio da 1-53.txt per farlo funzionare. Il cambiamento segue:

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

A pagina 90 del libro"Wrox professionale ASP.NET MVC 2" il codice è elencato come:

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

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

Ma il termine è:

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

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

Questo overload di metodo proverà ad aggiornare il modello specificato [Dinner], anziché il [ViewModel] predefinito, utilizzando i valori fr om il fornitore di valore del controllore. Fondamentalmente tutto ciò che fa è aggiungere un prefisso a tutti i tuoi valori quando li cerchi nel provider.

Così, quando il modello sta cercando di aggiornare la proprietà del titolo, cercherà Dinner.Title, anziché solo Title nel provider di valori del controller.

Durante il debug, dare un'occhiata al metodo Edit ActionResult ed esaminare il parametro di input FormCollection. Quando si scava giù in è di matrice di ingresso, troverete I tasti che iniziano tutti con il prefisso dell'oggetto di proprietà si fa riferimento nella vostra vista, nel tuo caso Edit View, in questo modo:

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