web-dev-qa-db-ja.com

MVC 3-Html.EditorForは$ .ajax呼び出しの後に古い値をキャッシュするようです

これは次の質問の続きです。

MVC 3 + $ .ajax-応答は部分的なビューからの出力をキャッシュしているようです

そこに問題の詳細な説明があります。しかし、私は今や問題を絞り込むことができました。それはHtml.EditorForヘルパーにあると思われるため、新しい質問です。

問題:

$ .ajaxを使用してサーバーにデータを投稿し、入力コントロールを保持する部分ビューのHTMLを返します。問題は、新しく作成されたオブジェクトをパーシャルビューモデルに渡しても、さまざまな@ Html.EditorForヘルパーと@ Html.DropDownListForヘルパーが古いデータを返すということです。

Htmlヘルパーの横に値を出力することで、モデルがヘルパーに新しいオブジェクトを正しく渡したことを証明できます。すなわち:

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

次の画像が示すように、@ Html.EditorForは間違ったデータを返します。

Cached response...

[Comentarioテキストボックスの横の値は日付時刻です。デフォルトの値を、投稿ごとに変化する値、つまりDateTimeで置き換えることをテストしていたためです。]

TransactionDateの@ Html.EditorForをプレーンな古い@ Html.TextBox()に置き換えた場合:

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

次に、新しいTransactionオブジェクトの正しいTransactionDate値、つまりDateTime.MinValue(01/01/0001 ...)をレンダリングします。

したがって...

問題は@ Html.EditorForヘルパーにあります。この問題は、TextBoxForおよびDropDownListForでも発生します。

問題は、これらのヘルパーが古い値をキャッシュしているように見えることです。

何がいけないの??!

編集:

日付のカスタムエディターテンプレートでデバッグを試みたところ、ViewData.TemplateInfo.FormattedModelValueに正しい値、つまり「01/01/0001」が表示されました。ただし、Fiddlerに到達すると、応答には古い日付が表示されます。たとえば、上の画像では "2011/09/2011"です。

結果として、ここではキャッシングが行われていると思いますが、何も設定していないので、何も意味がありません。

43
awrigley

ここにはcachingは含まれていません。これはHTMLヘルパーが機能する方法です。値をバインドするときに最初にModelStateを確認し、次にモデルを確認します。したがって、コントローラーアクション内のPOSTされた値のいずれかを変更する場合は、まずモデルの状態からそれらを削除してください。

[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);
}
105
Darin Dimitrov

キャッシュを指定しない場合でも、キャッシュが発生することがあります。 AJAXおよびJSONリクエストを処理するコントローラーの場合、次のように装飾します。

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

これは、キャッシングが発生しないことを明確に宣言しています。

[〜#〜]更新[〜#〜]

Darin Dimitrovの回答 here に基づいて、コントローラーアクションに次の行を追加してみてください。

ModelState.Clear();
20
counsellorben

私はこれを見たことはありませんが、基本的にajaxを使用してこのデータを要求している場合は、nochacheを設定する必要があります。

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

ちょうど暗闇の中で刺す、あなたはおそらくすでにこれをすでにカバーしていると思います。最初に新しいモデルを作成してから、そのモデルの新しいインスタンスにデータを入力してから、ビューに送信しようとしましたか?

最後に、どのDBサーバーを使用しているかわからないが、DBの結果がキャッシュされておらず、DBキャッシュからSQL結果を要求しているだけではないことを確認しました... MsSQLを使用していませんが、何かが出力されるまでoutputCaching DBサーバー自体の変更??とにかくいくつかの考え

1
davethecoder

これは予期しない動作であり、ModelStateを優先する必要がある理由は理解していますが、そのエントリを削除して、Modelの値が代わりに使用されるようにする方法が必要でした。

これを支援するために私が思いついた方法がいくつかあります。 RemoveStateForメソッドは、必要なプロパティのModelStateDictionary、モデル、および式を受け取り、それを削除します。

HiddenForModelをビューで使用して、最初にModelStateエントリを削除することにより、モデルの値のみを使用して非表示の入力フィールドを作成できます。 (これは他のヘルパー拡張メソッドのために簡単に拡張できます)。

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

次のようなコントローラーから呼び出します。

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

またはこのようなビューから:

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

それは使用しています System.Web.Mvc.ExpressionHelperは、ModelStateプロパティの名前を取得します。キー名が明確でないため、これは「ネストされた」モデルがある場合に特に役立ちます。

1
Tobias J

これを実行していないことを確認してください:

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

私はこれを行い、モデルは決して価値を取り戻しませんでした。 .Dateを削除すると、問題なく動作しました。

0
Bernard Perdomo