web-dev-qa-db-fra.com

MVC 3 - Html.EditorFor semble mettre en cache les anciennes valeurs après l'appel de $ .ajax

Ceci fait suite à la question suivante:

MVC 3 + $ .ajax - la réponse semble mettre en cache la sortie d'une vue partielle

Il y a une description détaillée du problème là-bas. Cependant, j'ai maintenant réussi à réduire le problème, cela semble être avec les aides Html.EditorFor, d'où la nouvelle question.

Le problème:

Je publie des données sur le serveur à l’aide de $ .ajax, puis renvoie le code HTML de la vue partielle contenant les contrôles d’entrée. Le problème est que, malgré le passage d'un objet nouvellement créé au modèle Vues partielles, les différents assistants @ Html.EditorFor et @ Html.DropDownListFor renvoient les OLD DATA !.

Je peux prouver que le modèle a correctement transmis un nouvel objet aux aides, en imprimant la valeur à côté de l’aide HTML. C'est à dire:

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

Comme le montre l'image suivante, le @ Html.EditorFor renvoie les mauvaises données:

Cached response...

[Notez que la valeur en regard de la zone de texte Comentario est une date, car je testais le remplacement des valeurs par défaut par une valeur qui changerait à chaque publication, c'est-à-dire un DateTime.]

Si je remplace le @ Html.EditorFor pour TransactionDate par un vieil ancien @ Html.TextBox ():

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

Ensuite, il affiche la valeur TransactionDate correcte pour un nouvel objet Transaction, par exemple, DateTime.MinValue (01/01/0001 ...).

Donc...

Le problème vient des aides @ Html.EditorFor. Le problème se produit également avec TextBoxFor et DropDownListFor.

Le problème étant que ces assistants semblent mettre en cache l'ancienne valeur.

Qu'est-ce que je fais mal??!

MODIFIER:

Je viens d’essayer de déboguer dans le modèle d’éditeur personnalisé pour les dates, puis dans ViewData.TemplateInfo.FormattedModelValue indique la valeur correcte, c’est-à-dire "01/01/0001". Cependant, une fois qu’il parvient à Fiddler, la réponse affiche l’ancienne date, par exemple, "01/09/2011" dans l’image ci-dessus.

En conséquence, je pense simplement qu'il y a une certaine mise en cache qui se passe ici, mais je n'en ai aucune mise en place, donc rien n'a de sens.

41
awrigley

Il n'y a pas de mise en cache impliqué ici. C'est juste comment HTML assistant fonctionne. Ils examinent d'abord le ModelState lorsqu'ils lient leurs valeurs, puis dans le modèle. Par conséquent, si vous envisagez de modifier les valeurs POSTed au sein de l'action de votre contrôleur, veillez à les supprimer de l'état du modèle:

[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

Même si vous ne spécifiez pas la mise en cache, cela peut parfois se produire. Pour mes contrôleurs qui gèrent AJAX et les requêtes JSON, je les décore comme suit:

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

Cela déclare spécifiquement qu'aucune mise en cache ne doit avoir lieu.

METTRE À JOUR

Sur la base d'une réponse, Darin Dimitrov a donné ici , essayez d'ajouter la ligne suivante à l'action de votre contrôleur:

ModelState.Clear();
18
counsellorben

je n'ai jamais vu cela, mais si vous utilisez ajax pour demander ces données, vous devez définir nochache: je suppose que vous utilisez jQuery.ajax ici pour afficher le code:

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

juste un coup de poignard dans le noir, je suppose que vous avez probablement déjà déjà couvert cela. Avez-vous d'abord essayé de créer un nouveau modèle, puis de renseigner cette nouvelle instance du modèle avec vos données, puis de l'envoyer à votre vue!

Enfin, vous ne savez pas quel serveur de base de données vous utilisez, mais avez-vous vérifié que les résultats de la base de données ne sont pas mis en cache et que vous ne demandez pas uniquement des résultats SQL à partir du cache de la base de données ... changer sur le serveur de base de données lui-même? de toute façon juste quelques réflexions

1
davethecoder

C'était un comportement inattendu pour moi, et bien que je comprenne la raison pour laquelle il est nécessaire de donner la priorité ModelState, j'avais besoin d'un moyen de supprimer cette entrée pour que la valeur de Model soit utilisée à la place.

Voici quelques méthodes que j'ai proposées pour vous aider. La méthode RemoveStateFor prendra une ModelStateDictionary, un modèle et une expression pour la propriété souhaitée, puis la supprimera.

HiddenForModel peut être utilisé dans votre vue pour créer un champ de saisie masqué en utilisant uniquement la valeur du modèle, en supprimant d'abord son entrée ModelState. (Cela pourrait facilement être étendu pour les autres méthodes d'extension d'assistance).

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

Appel d'un contrôleur comme celui-ci:

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

ou d'une vue comme celle-ci:

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

Il utilise System.Web.Mvc.ExpressionHelper pour obtenir le nom de la propriété ModelState. Ceci est particulièrement utile lorsque vous avez des modèles "imbriqués" car le nom de la clé n’est pas évident.

1
Toby J

Assurez-vous que vous ne faites pas ceci:

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

Je l'ai fait, et le modèle n'a jamais récupéré la valeur. Cela a fonctionné parfaitement une fois que je supprime le .Date.

0
Bernard Perdomo