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

ASP.NET Core 6 MVC用TempData跨页传参遇空引用异常求助

问题分析与解决方案

为什么TempData会触发空引用异常

TempData的设计是仅保留一次请求的数据,当你从Admin/Index页面跳转到其他视图时,TempData中存储的YearList和CurrentYear会被自动清除,导致后续视图访问时为null,触发空引用错误。另外你在_ViewStart.cshtml里初始化TempData["YearList"] = null,反而会覆盖有效数据,加重问题。

用TempData实现的可行方案

如果一定要用TempData,需要手动保留数据,让它能跨多次请求:

  1. 在AdminController的Index方法中,存储数据后用Keep()标记需要保留的键:
public async Task<IActionResult> Index()
{
    var allYears = await _context.Years.ToListAsync();
    List<SelectListItem> yearList = new List<SelectListItem>();
    foreach (var item in allYears)
    {
        yearList.Add(new SelectListItem { Text = item.Name, Value = item.Id.ToString() });
    }
    TempData["YearList"] = yearList;
    TempData["CurrentYear"] = allYears.FirstOrDefault()?.Id.ToString(); // 空值保护
    // 标记需要保留的键,避免一次请求后被清除
    TempData.Keep("YearList");
    TempData.Keep("CurrentYear");
    return View();
}
  1. 在_Layout.cshtml中访问TempData时,使用Peek()获取数据(不会触发清除),同时添加空值判断:
@if (TempData.Peek("YearList") is List<SelectListItem> yearList && TempData.Peek("CurrentYear") is string currentYear)
{
    <select class="form-control" asp-for="@currentYear">
        @foreach (var item in yearList)
        {
            <option value="@item.Value" @(item.Value == currentYear ? "selected" : "")>@item.Text</option>
        }    
    </select>
}
  1. 当用户选择年份提交时,在处理提交的Action中更新TempData并再次调用Keep():
public IActionResult SetYear(int yearId)
{
    // 获取年份列表(可以从数据库重新获取,或者从TempData.Peek获取)
    var yearList = TempData.Peek("YearList") as List<SelectListItem>;
    TempData["CurrentYear"] = yearId.ToString();
    TempData["YearList"] = yearList;
    TempData.Keep("YearList");
    TempData.Keep("CurrentYear");
    return RedirectToAction("Index", "Home"); // 跳转到目标页面
}

注意:即使这样,TempData仍然会在浏览器关闭后丢失数据,且跨标签页不共享,不是最优解。

更优方案:使用ViewComponent

ViewComponent是ASP.NET Core中用于复用视图逻辑的组件,适合处理这种全局的下拉列表:

  1. 创建YearSelectorViewComponent:
public class YearSelectorViewComponent : ViewComponent
{
    private readonly YourDbContext _context;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public YearSelectorViewComponent(YourDbContext context, IHttpContextAccessor httpContextAccessor)
    {
        _context = context;
        _httpContextAccessor = httpContextAccessor;
    }

    public async Task<IViewComponentResult> InvokeAsync()
    {
        // 从Session获取当前选中的年份,默认取第一个
        var currentYearId = _httpContextAccessor.HttpContext.Session.GetString("CurrentYear");
        var allYears = await _context.Years.ToListAsync();
        var yearList = allYears.Select(y => new SelectListItem 
        { 
            Text = y.Name, 
            Value = y.Id.ToString(),
            Selected = y.Id.ToString() == currentYearId
        }).ToList();

        // 如果没有选中的年份,设置默认值
        if (string.IsNullOrEmpty(currentYearId) && yearList.Any())
        {
            currentYearId = yearList.First().Value;
            _httpContextAccessor.HttpContext.Session.SetString("CurrentYear", currentYearId);
        }

        return View(yearList);
    }
}
  1. 创建ViewComponent的视图(Views/Shared/Components/YearSelector/Default.cshtml):
@model List<SelectListItem>
<form asp-action="SetYear" asp-controller="Admin" method="post">
    <select class="form-control" name="yearId" onchange="this.form.submit()">
        @foreach (var item in Model)
        {
            <option value="@item.Value" @(item.Selected ? "selected" : "")>@item.Text</option>
        }
    </select>
</form>
  1. 在_Layout.cshtml中调用ViewComponent:
@await Component.InvokeAsync("YearSelector")
  1. 添加SetYear的Action处理提交:
[HttpPost]
public IActionResult SetYear(int yearId)
{
    HttpContext.Session.SetString("CurrentYear", yearId.ToString());
    return Redirect(Request.Headers["Referer"].ToString()); // 返回到之前的页面
}
  1. 确保Startup/Program中启用Session:
// Program.cs
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromHours(2); // 设置Session超时时间
});

app.UseSession(); // 放在UseRouting之后,UseAuthorization之前

其他补充建议

  • 避免在_ViewStart.cshtml中初始化TempData/Session,因为它会在每个视图渲染前执行,可能覆盖用户的选择。
  • 所有需要根据年份获取数据的Action,都可以从Session中读取当前年份:
var currentYearId = HttpContext.Session.GetString("CurrentYear");
if (!string.IsNullOrEmpty(currentYearId) && int.TryParse(currentYearId, out int yearId))
{
    // 根据yearId查询对应数据
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:35:34