it-swarm.it

MVC 3 - Html.EditorFor sembra memorizzare i vecchi valori dopo la chiamata $ .ajax

Questo è un seguito dalla seguente domanda:

MVC 3 + $ .ajax - la risposta sembra essere l'output del caching dalla vista parziale

C'è una descrizione dettagliata del problema laggiù. Tuttavia, ora sono riuscito a restringere il problema, che sembra essere con gli helper Html.EditorFor, da qui la nuova domanda.

Il problema:

Inserisco i dati sul server usando $ .ajax, quindi restituisco l'html della vista parziale che contiene i controlli di input. Il problema è che, nonostante il passaggio di un oggetto appena creato al modello Viste parziali, i vari helper @ Html.EditorFor e @ Html.DropDownListFor restituiscono i VECCHI DATI !.

Posso provare che il modello è passato correttamente in un nuovo oggetto agli helper, stampando il valore accanto all'helper Html. Vale a dire:

@Html.EditorFor(model => model.Transaction.TransactionDate) 
@Model.Transaction.TransactionDate.ToString()

Come mostra la seguente immagine, @ Html.EditorFor sta restituendo i dati errati:

Cached response...

[Si noti che il valore accanto alla casella di testo Comentario è un orario, perché stavo testando la sostituzione dei valori predefiniti con un valore che sarebbe cambiato con ogni post, cioè un DateTime.]

Se sostituisco @ Html.EditorFor per TransactionDate con un semplice vecchio @ Html.TextBox ():

@Html.TextBox("Transaction_TransactionDate", Model.Transaction.TransactionDate)

Quindi esegue il rendering del valore TransactionDate corretto per un nuovo oggetto Transaction, ad esempio DateTime.MinValue (01/01/0001 ...).

Perciò...

Il problema è con gli helper @ Html.EditorFor. Il problema si verifica anche con TextBoxFor e DropDownListFor.

Il problema è che questi helper sembrano memorizzare nella cache il vecchio valore.

Che cosa sto facendo di sbagliato??!

MODIFICARE:

Ho appena provato il debug nel modello di editor personalizzato per le date e, in questo, ViewData.TemplateInfo.FormattedModelValue mostra il valore corretto, ovvero "01/01/0001". Tuttavia, una volta arrivato a Fiddler, la risposta mostra la vecchia data, ad esempio "01/09/2011" nell'immagine sopra.

Di conseguenza, penso solo che ci sia del caching in corso qui, ma non ne ho creato nessuno, quindi niente ha senso.

41
awrigley

Non c'è caching coinvolto qui. È proprio come funziona l'helper HTML. Esaminano prima ModelState quando vincolano i loro valori e quindi nel modello. Pertanto, se si intende modificare i valori POST all'interno dell'azione del controller, assicurarsi di rimuoverli prima dallo stato del modello:

[HttpPost]
public virtual ActionResult AjaxCreate(Transaction transaction)
{
    if (ModelState.IsValid)
    {
        service.InsertOrUpdate(transaction);
        service.Save();
    }
    service.ChosenCostCentreId = transaction.IdCostCentre;
    TransactionViewModel viewModel = new TransactionViewModel();
    ModelState.Remove("Transaction");
    viewModel.Transaction = new Transaction();
    ModelState.Remove("CostCentre");
    viewModel.CostCentre = service.ChosenCostCentre;
    ...

    return PartialView("_Create", viewModel);
}
102
Darin Dimitrov

Anche se non si specifica la memorizzazione nella cache, a volte può verificarsi. Per i miei controller che gestiscono le richieste AJAX e JSON, le abbellisco come segue:

[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]

Questo in particolare dichiara che non dovrebbe verificarsi la memorizzazione nella cache.

UPDATE

Sulla base di una risposta, Darin Dimitrov ha dato qui , prova ad aggiungere la seguente riga all'azione del tuo controller:

ModelState.Clear();
18
counsellorben

non l'ho mai visto ma fondamentalmente se stai usando ajax per richiedere questi dati, devi impostare nochache: sto assumendo che tu stia usando jQuery.ajax qui così mostrerà il codice:

$.ajax({
    url: "somecontroller/someAction,
    cache: false, // this is key to make sure JQUERY does not cache your request
    success: function( data ) {  
         alert( data );
    }
});

solo una pugnalata al buio, presumo tu abbia già già coperto questo già. hai provato a creare un nuovo modello per primo e poi a popolare la nuova istanza del modello con i tuoi dati, e poi inviarlo alla tua vista!

Infine non si è sicuri di quale server DB si sta usando, ma si è verificato che i risultati del DB non siano memorizzati nella cache e che non si stiano richiedendo solo risultati SQL dalla cache del DB ... non uso MsSQL ma ho sentito che ha outputCaching finché qualcosa non è cambiare sul server DB stesso ?? comunque solo alcuni pensieri

1
davethecoder

Questo è stato un comportamento inaspettato per me, e anche se capisco il motivo per cui è necessario dare precedenza a ModelState, avevo bisogno di un modo per rimuovere quella voce in modo da utilizzare invece il valore di Model.

Qui ci sono un paio di metodi che ho trovato per aiutare con questo. Il metodo RemoveStateFor prenderà un ModelStateDictionary, un modello e un'espressione per la proprietà desiderata e la rimuoverà.

HiddenForModel può essere utilizzato nella vista per creare un campo di input nascosto utilizzando solo il valore del modello, rimuovendo prima la sua voce ModelState. (Questo potrebbe essere facilmente esteso per gli altri metodi di estensione helper).

/// <summary>
/// Returns a hidden input field for the specified property. The corresponding value will first be removed from
/// the ModelState to ensure that the current Model value is shown.
/// </summary>
public static MvcHtmlString HiddenForModel<TModel, TProperty>(this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression)
{
    RemoveStateFor(helper.ViewData.ModelState, helper.ViewData.Model, expression);
    return helper.HiddenFor(expression);
}

/// <summary>
/// Removes the ModelState entry corresponding to the specified property on the model. Call this when changing
/// Model values on the server after a postback, to prevent ModelState entries from taking precedence.
/// </summary>
public static void RemoveStateFor<TModel, TProperty>(this ModelStateDictionary modelState, TModel model,
    Expression<Func<TModel, TProperty>> expression)
{
    var key = ExpressionHelper.GetExpressionText(expression);

    modelState.Remove(key);
}

Chiama da un controller come questo:

ModelState.RemoveStateFor(model, m => m.MySubProperty.MySubValue);

o da una vista come questa:

@Html.HiddenForModel(m => m.MySubProperty.MySubValue)

Usa System.Web.Mvc.ExpressionHelper per ottenere il nome della proprietà ModelState. Ciò è particolarmente utile quando si dispone di modelli "annidati" poiché il nome della chiave non è evidente.

1
Toby J

Assicurati di non farlo:

@Html.EditorFor(model => model.Transaction.TransactionDate.Date)

L'ho fatto e il modello non ha mai ottenuto il valore. Ha funzionato perfettamente una volta rimosso il .Date.

0
Bernard Perdomo