You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core MVC中PRG模式高性能实现:保留输入与验证信息

ASP.NET Core MVC多页表单PRG模式性能疑问解答

背景与代码实现

我基于ASP.NET Core MVC构建了一个用于提交和更新活动申请的多页表单,采用POST-Redirect-GET(PRG)模式,需保留输入内容与验证错误信息。表单每页包含主办方管理的展示信息(如场地列表)和用户输入信息(如用户选择的场地)。现有可运行实现,但对性能存疑,代码如下:

using System.Text.Json;
using Microsoft.AspNetCore.Mvc;

public class ApplicationController : Controller
{
    [HttpGet]
    public async Task<IActionResult> Step1(int id)
    {
        this.TempData.TryGetValue("viewModel", out Object? viewModelJSON);
        Step1ViewModel viewModel;
        if (viewModelJSON == null)
        {
           // create empty view model
           viewModel = new Step1ViewModel();
           viewModel.ID = id;
           // when simple GET: show data from db
           var userInputModel = await this.LoadVenueChoicesAsync(id);
           // fill view model if data exists
           if (userInputModel != null) {
              viewModel.UserInput = BuildUserInputViewModel(userInputModel);
           }
        }
        else
        {
            // when GET after POST: show input
            viewModel = JsonSerializer.Deserialize<Step1ViewModel>(viewModelJSON);
            await this.TryValidateModelAsync(viewModel);
        }
        var venueList = await this.LoadVenuesAsync();
        viewModel.Information = BuildInformationViewModel(venueList);

        return this.View(viewModel);
    }

    [HttpPost]
    public async Task<IActionResult> Step1(Step1ViewModel viewModel)
    {
        if (this.ModelState.IsValid == false)
        {
            var viewModelJSON = JsonSerializer.Serialize<Step1ViewModel>(viewModel);
            this.TempData["viewModel"] = viewModelJSON;
            return this.RedirectToAction("Step1", new { id = viewModel.ID });
        }
        else
        {
            await this.StoreVenueChoicesAsync(viewModel.UserInput);
            return this.RedirectToAction("Step2", new { id = viewModel.ID });
        }
    }
}

疑问解答

1. JSON序列化是否会引发性能问题?是否有其他替代方案?

  • 常规场景下,System.Text.Json的序列化/反序列化性能足以应对高并发,只有当Step1ViewModel包含超大对象(如大文本、二进制数据)时,才会出现明显性能损耗。
  • 替代方案:
    • 仅序列化用户输入部分:当前序列化了整个ViewModel,但展示信息(Information)可在GET请求时重新从数据库加载,只需保留UserInput和验证错误相关数据,减少序列化体积。
    • 序列化ModelState而非整个ViewModel:直接将验证状态ModelState序列化存入TempData,避免冗余数据。示例代码:
      // POST时保存ModelState
      TempData["ModelState"] = JsonSerializer.Serialize(ModelState);
      // GET时恢复ModelState
      if (TempData.ContainsKey("ModelState"))
      {
          var serializedModelState = TempData["ModelState"].ToString();
          var modelState = JsonSerializer.Deserialize<ModelStateDictionary>(serializedModelState);
          ModelState.Merge(modelState);
      }
      
    • 二进制序列化库:若需序列化大对象,可使用MessagePack这类二进制序列化库,性能优于JSON,但需额外引入依赖。

2. TempData依赖Session,是否需要调用LoadAsync()?

  • 需要。TempData默认依赖Session存储,在读取TempData前调用await HttpContext.Session.LoadAsync()可避免重复读取Session存储,提升高并发场景下的性能,尤其是使用分布式Session时。
  • 修改后的GET方法示例:
    [HttpGet]
    public async Task<IActionResult> Step1(int id)
    {
        await HttpContext.Session.LoadAsync(); // 先加载Session
        this.TempData.TryGetValue("viewModel", out Object? viewModelJSON);
        // 后续逻辑不变
    }
    
  • 另外,高并发场景建议使用分布式Session(如Redis)替代内存Session,避免多服务器部署时的数据一致性问题。

3. 此方案还存在其他问题吗?

  • TempData过期问题:TempData默认单次读取后即被删除,用户刷新页面会丢失输入和错误信息。可通过TempData.Keep("viewModel")读取后保留数据,但需注意避免数据冗余。
  • 内存与传输开销:序列化整个ViewModel会增加服务器内存消耗(分布式场景下还会增加网络传输量),建议仅序列化必要的用户输入数据。
  • 重复验证与查询:POST时已完成Model验证,GET时无需再次调用TryValidateModelAsync;展示信息若不常变化,可通过IDistributedCache缓存减少数据库查询次数。
  • 分布式部署一致性:若应用部署在多台服务器上,必须使用分布式Session,否则TempData无法跨服务器读取,导致用户数据丢失。

内容的提问来源于stack exchange,提问作者pikachu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:30:53