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

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(打数)两个整数属性
  • 功能需求:输入球员姓名和系列赛场次后,通过一组文本框逐场收集单场数据,每次提交后仅保留一组输入框,待场次耗尽后自动计算打击率

遇到的问题

  • 重复调用AddGame Action及渲染视图时,length(剩余场次)值无法持续递减,模型数据无法跨请求保留
  • 尝试用Hidden字段传递数据,效果不佳;循环渲染多组文本框不符合"仅显示一组输入框"的需求

解决方案:使用.NET Core Session状态管理

通过Session存储跨请求的状态数据,实现数据持久化与累加:

  1. 配置Session中间件,确保请求间数据可存储
  2. 在Start Action中初始化Session,存储球员姓名、总场次、空Game集合
  3. 在Totalize Action中更新Session内的剩余场次和已提交的Game数据,通过ViewBag传递必要状态到视图
  4. 场次耗尽后,从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 17:20:36