ASP.NET MVC视图重复调用Action时模型数据保留问题及解决
解决.NET Core击球手打击率计算应用的状态持久化问题
项目背景
- 基于.NET Core开发的击球手打击率计算Web应用,运行于RedHat Linux环境,使用Visual Studio Code开发、Google Chrome测试
- 核心模型定义:
Batter类:包含average(打击率)、name(球员姓名)、length(系列赛场次)属性,以及List<Game>类型的series集合Game类:包含hits(安打数)、atbats(打数)两个整数属性
- 功能需求:输入球员姓名和系列赛场次后,通过一组文本框逐场收集单场数据,每次提交后仅保留一组输入框,待场次耗尽后自动计算打击率
遇到的问题
- 重复调用
AddGameAction及渲染视图时,length(剩余场次)值无法持续递减,模型数据无法跨请求保留 - 尝试用Hidden字段传递数据,效果不佳;循环渲染多组文本框不符合"仅显示一组输入框"的需求
解决方案:使用.NET Core Session状态管理
通过Session存储跨请求的状态数据,实现数据持久化与累加:
- 配置Session中间件,确保请求间数据可存储
- 在
StartAction中初始化Session,存储球员姓名、总场次、空Game集合 - 在
TotalizeAction中更新Session内的剩余场次和已提交的Game数据,通过ViewBag传递必要状态到视图 - 场次耗尽后,从Session读取累计数据计算打击率,完成后清空Session避免干扰后续请求
核心代码示例
1. Startup.cs配置Session
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddSession(options => { options.IdleTimeout = TimeSpan.FromMinutes(30); options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件(如静态文件、错误处理)... app.UseRouting(); app.UseSession(); // 必须在UseRouting之后、UseEndpoints之前 app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
2. HomeController关键逻辑
using System.Text.Json; using Microsoft.AspNetCore.Mvc; public class HomeController : Controller { public IActionResult Index() => View(); [HttpPost] public IActionResult Start(Batter batter) { if (ModelState.IsValid && batter.length > 0) { // 初始化Session数据 HttpContext.Session.SetString("BatterName", batter.name); HttpContext.Session.SetInt32("RemainingGames", batter.length); HttpContext.Session.SetString("GamesData", JsonSerializer.Serialize(new List<Game>())); return RedirectToAction("AddGame"); } return View("Index"); } public IActionResult AddGame() { var remainingGames = HttpContext.Session.GetInt32("RemainingGames"); if (remainingGames == null || remainingGames <= 0) { return RedirectToAction("CalculateAverage"); } ViewBag.RemainingGames = remainingGames; ViewBag.BatterName = HttpContext.Session.GetString("BatterName"); return View(); } [HttpPost] public IActionResult Totalize(Game game) { if (ModelState.IsValid && game.atbats > 0) { // 读取并更新Session中的Game集合 var gamesJson = HttpContext.Session.GetString("GamesData"); var games = JsonSerializer.Deserialize<List<Game>>(gamesJson); games.Add(game); HttpContext.Session.SetString("GamesData", JsonSerializer.Serialize(games)); // 更新剩余场次 var remaining = HttpContext.Session.GetInt32("RemainingGames") - 1; HttpContext.Session.SetInt32("RemainingGames", remaining); return remaining > 0 ? RedirectToAction("AddGame") : RedirectToAction("CalculateAverage"); } ViewBag.RemainingGames = HttpContext.Session.GetInt32("RemainingGames"); ViewBag.BatterName = HttpContext.Session.GetString("BatterName"); return View("AddGame"); } public IActionResult CalculateAverage() { var gamesJson = HttpContext.Session.GetString("GamesData"); var games = JsonSerializer.Deserialize<List<Game>>(gamesJson); var totalHits = games.Sum(g => g.hits); var totalAtBats = games.Sum(g => g.atbats); var average = totalAtBats > 0 ? Math.Round((double)totalHits / totalAtBats, 3) : 0; var batter = new Batter { name = HttpContext.Session.GetString("BatterName"), average = average, series = games }; HttpContext.Session.Clear(); // 清空Session,避免后续请求干扰 return View(batter); } } // 模型类定义 public class Batter { public double average { get; set; } public string name { get; set; } public int length { get; set; } public List<Game> series { get; set; } } public class Game { public int hits { get; set; } public int atbats { get; set; } }
3. AddGame视图(Razor)
@{ ViewData["Title"] = "输入单场数据"; } <h1>@ViewBag.BatterName 的第 @(ViewBag.RemainingGames) 场比赛</h1> <form asp-action="Totalize"> <div class="form-group"> <label>安打数:</label> <input type="number" name="hits" min="0" required class="form-control" /> </div> <div class="form-group"> <label>打数:</label> <input type="number" name="atbats" min="1" required class="form-control" /> </div> <button type="submit" class="btn btn-primary">提交本场数据</button> </form>
内容的提问来源于stack exchange,提问作者Dave
相关产品推荐
相关产品推荐

