MVC 3 - Html.EditorFor 似乎在 $.ajax 调用后缓存旧值

发布于 2024-12-04 21:42:47 字数 1386 浏览 0 评论 0原文

这是以下问题的后续内容:

MVC 3 + $.ajax - 响应似乎是缓存部分视图的输出

那里有问题的详细描述。但是,我现在已经成功缩小了问题范围,这似乎与 Html.EditorFor 帮助程序有关,因此出现了新问题。

问题:

我使用 $.ajax 将数据发布到服务器,然后返回包含输入控件的部分视图的 html。问题在于,尽管将新创建的对象传递给部分视图模型,各种 @Html.EditorFor 和 @Html.DropDownListFor 帮助程序仍返回旧数据!

我可以通过在 Html 帮助器旁边打印该值来证明模型已正确地将新对象传递给帮助器。即:

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

如下图所示,@Html.EditorFor 返回错误的数据:

缓存响应...

[注意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,响应就会显示旧日期,例如上图中的“01/09/2011”。

结果,我只是认为这里正在进行一些缓存,但我没有设置任何缓存,所以没有任何意义。

This is a follow on from the following question:

MVC 3 + $.ajax - response seems to be caching output from partial view

There is a detailed description of the problem over there. However, I have now managed to narrow down the problem, that seems to be with the Html.EditorFor helpers, hence the new question.

The issue:

I post data to the server using $.ajax, then return the html of the partial view that holds the input controls. The problem is that, despite passing a newly created object to the Partial Views model, the various @Html.EditorFor and @Html.DropDownListFor helpers return the OLD DATA!.

I can prove that the model has correctly passed in a new object to the helpers, by printing the value out beside the Html helper. Ie:

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

As the following image shows, the @Html.EditorFor is returning the wrong data:

Cached response...

[Note that the value beside the Comentario text box is a date time, because I was testing replacing the default values with a value that would change with each post, ie, a DateTime.]

If I replace the @Html.EditorFor for TransactionDate with a plain old @Html.TextBox():

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

Then it renders the correct TransactionDate value for a new Transaction object, ie, DateTime.MinValue (01/01/0001...).

Therefore...

The problem is with the @Html.EditorFor helpers. The problem also happens with TextBoxFor and DropDownListFor.

The problem being that these helpers seem to cache the old value.

What am I doing wrong??!

EDIT:

I have just tried debugging in the custom Editor template for dates, and in there, ViewData.TemplateInfo.FormattedModelValue shows the correct value, ie, "01/01/0001". However, once it gets to Fiddler, the response is showing the old date, eg, "01/09/2011" in the image above.

As a result, I just think that there is some caching going on here, but I have none set up, so nothing makes any sense.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

木有鱼丸 2024-12-11 21:42:47

这里不涉及缓存。这就是 HTML 帮助器的工作原理。他们在绑定值时首先查看 ModelState,然后在模型中查看。因此,如果您打算修改控制器操作中的任何 POSTed 值,请确保首先将它们从模型状态中删除:

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

There is no caching involved here. It's just how HTML helper work. They first look at the ModelState when binding their values and then in the model. So if you intend to modify any of the POSTed values inside your controller action make sure you remove them from the model state first:

[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);
}
苹果你个爱泡泡 2024-12-11 21:42:47

即使您不指定缓存,有时也会发生这种情况。对于处理 AJAX 和 JSON 请求的控制器,我将它们装饰如下:

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

这明确声明不应发生缓存。

更新

基于Darin Dimitrov给出的答案此处,尝试将以下行添加到您的控制器操作中:

ModelState.Clear();

Even if you do not specify caching, it sometimes can occur. For my controllers which handle AJAX and JSON requests, I decorate them as follows:

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

This specifically declares no caching should occur.

UPDATE

Based on an answer Darin Dimitrov gave here, try adding the following line to your controller action:

ModelState.Clear();
蓦然回首 2024-12-11 21:42:47

我从来没有见过这个,但基本上如果你使用ajax来请求这些数据,你需要设置nochache:我假设你在这里使用jQuery.ajax,所以会显示代码:

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

只是在黑暗中刺伤,我假设你可能有已经涵盖了这一点。您是否尝试先创建一个新模型,然后用您的数据填充该模型的新实例,然后将其发送到您的视图!

最后不确定您使用的是什么数据库服务器,但您是否检查过数据库结果是否未缓存,并且您不仅仅是从数据库缓存请求 SQL 结果...我不使用 MsSQL,但我听说它具有输出缓存,直到有东西出现为止数据库服务器本身发生变化?不管怎样,只是一些想法

i have never seen this but basically if you are using ajax to request this data, you need to set nochache: i am assuming you using jQuery.ajax here so will show the code:

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

just a stab in the dark, i assume you have probably already covered this already. have you tried to create a new model first and then populate that new instance of the model with your data, and then send this to your view!

Finally not sure what DB server your using but have you check to see that DB results are not cached and that you are not just requesting SQL results from the DB cache... i dont use MsSQL but i hear that it has outputCaching until something is change on the DB server itself?? anyway just a few thoughts

友谊不毕业 2024-12-11 21:42:47

这对我来说是意想不到的行为,尽管我理解为什么需要给予 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 属性的名称。当您有“嵌套”模型时,这特别有用,因为键名称不明显。

This was unexpected behavior for me, and although I understand the reason why it's necessary to give ModelState precedence, I needed a way to remove that entry so that the value from Model is used instead.

Here are a couple methods I came up with to assist with this. The RemoveStateFor method will take a ModelStateDictionary, a Model, and an expression for the desired property, and remove it.

HiddenForModel can be used in your View to create a hidden input field using only the value from the Model, by first removing its ModelState entry. (This could easily be expanded for the other helper extension methods).

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

Call from a controller like this:

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

or from a view like this:

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

It uses System.Web.Mvc.ExpressionHelper to get the name of the ModelState property. This is especially useful when you have "Nested" models since the key name isn't obvious.

心奴独伤 2024-12-11 21:42:47

确保你没有这样做:

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

我这样做了,但模型从未得到回值。当我删除 .Date 后,它就完美地工作了。

Make sure you're not doing this:

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

I did this, and the model never got the value back. It worked perfectly once I remove the .Date.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文