在重新显示无效ModelState的视图时,在表单上保留模型数据

时间:2011-09-26 19:42:36

标签: asp.net-mvc-3

简化示例:

[HttpGet]
public ActionResult Report(DateTime? date)
{
    if (!date.HasValue)
    {
        date = DateTime.Now;
    }

    // Clear the ModelState so that the date is displayed in the correct format as specified by the DisplayFormat attribute on the model
    ModelState.Clear();

    // Expensive call to database to retrieve the report data
    ReportModel model = DataAdapter.Convert(this.reportService.GetReport((DateTime)date));

    // Store in TempData in case validation fails on HttpPost
    TempData["ReportModel"] = model;

    return View(model);
}

[HttpPost]
public ActionResult Report(ReportModel model)
{
    if (ModelState.IsValid)
    {
        return RedirectToAction("Report", new { date = model.Date });
    }
    else
    {
        // Load the report from TempData, and restore again in case of another validation failure
        model = TempData["ReportModel"] as ReportModel;
        TempData["ReportModel"] = model;

        // Redisplay the view, the user inputted value for date will be loaded from ModelState
        return View(model);
    }
}

我的问题是,我是否以正确的方式将报告数据存储在TempData中?代码看起来有点奇怪,特别是在HttpPost操作方法中读取然后写回TempData,以确保它持久保存下一个请求。

我能想到的其他选择是:

(1)从HttpPost操作再次调用服务层(我不想再进行另一次数据库调用,因为验证失败只是为了重新显示表单,因为它似乎效率低下)。我想我可以在服务层实现缓存,以避免数据库往返...

(2)在表格中使用隐藏的输入(yuk!)。

(3)将最近查看的报告永久存储在会话中。

其他人怎么做这种事情?建议的做法是什么?

1 个答案:

答案 0 :(得分:3)

  

我的问题是,我是否以正确的方式将报告数据存储在TempData中?

不,绝对没有。当且仅当您之后立即重定向时,将某些内容存储到TempData中,因为TempData仅存在一次重定向。如果您在GET操作中将某些内容存储到TempData中然后呈现视图,那么例如来自此视图的AJAX请求将终止TempData,并且您将不会在POST请求中获取值。

正确的模式如下(无TempData):

public ActionResult Report(DateTime? date)
{
    if (!date.HasValue)
    {
        date = DateTime.Now;
    }

    // Clear the ModelState so that the date is displayed in the correct format as specified by the DisplayFormat attribute on the model
    ModelState.Clear();

    // Expensive call to database to retrieve the report data
    ReportModel model = DataAdapter.Convert(this.reportService.GetReport((DateTime)date));

    return View(model);
}

[HttpPost]
public ActionResult Report(ReportModel model)
{
    if (ModelState.IsValid)
    {
        return RedirectToAction("Report", new { date = model.Date });
    }
    else
    {
        // Redisplay the view, the user inputted value for date will be loaded from ModelState
        // TODO: query the database/cache to refetch any fields that weren't present
        // in the form and that you need when redisplaying the view
        return View(model);
    }
}
  

(1)从HttpPost操作再次调用服务层   (由于验证,我宁愿不再进行另一次数据库调用   失败只是为了重新显示表单,因为它似乎效率低下)。我想我   可以在服务层实现缓存以避免数据库   往返......

这正是你应该做的。如果您在优化这些查询时遇到问题,或者在每个POST请求中遇到您的问题,那么就会缓存这些结果。数据库现在是超优化的,并且设计用于完成此操作(当然不要滥用,在适当的列上定义索引并且性能应该很好)。但是,如果您的网站要求很高,并且有很多请求和用户,那么缓存当然是避免访问数据库的最佳方法。

  

(2)在表格中使用隐藏的输入(yuk!)。

Yuk,我同意,但可以在你没有很多的情况下工作。

  

(3)将最近查看的报告永久存储在会话中。

不,避免会话。 Session是可扩展和无状态应用程序的敌人。