从Session获取发票单元列表异常:仅返回首个单元的问题排查
问题描述
我正在开发发票功能:已登录用户的发票及发票单元(一对多关系)存储至数据库,未登录用户的数据则存入Session。调试发现,Create方法中invoiceUnitsFromSession的单元数量正确,但在Edit控制器调用GetInvoiceUnitsFromSession()时,每个发票仅能获取到首个发票单元。
现有代码
Create方法
[HttpPost] public ActionResult Create(Invoice invoice) { var userId = User.Claims.FirstOrDefault(c => c.Type == "UserId")?.Value; if (userId is not null) { invoice.UserId = Guid.Parse(userId.ToString()); DateTime dateTime = DateTime.Now; invoice.DateCreated = dateTime; foreach (var invoiceUnit in invoice.InvoiceUnits) { db.InvoiceUnits.Add(invoiceUnit); } db.Add(invoice); db.SaveChanges(); return RedirectToAction("Index"); } else { invoice.Id = Guid.NewGuid(); var invoicesFromSession = GetInvoicesFromSession(); var invoiceUnitsFromSession = GetInvoiceUnitsFromSession(); DateTime dateTime = DateTime.Now; invoice.DateCreated = dateTime; if (invoice is not null) { invoicesFromSession.Add(invoice); HttpContext.Session.SetString("invoice", JsonSerializer.Serialize(invoicesFromSession)); foreach(var invoiceUnit in invoice.InvoiceUnits) { invoiceUnit.Id = new Guid(); invoiceUnit.InvoiceId = invoice.Id; HttpContext.Session.SetString("invoiceUnit", JsonSerializer.Serialize(invoiceUnitsFromSession)); invoiceUnitsFromSession.Add(invoiceUnit); } } return RedirectToAction("Index"); } }
从Session获取发票单元的方法
private List<InvoiceUnit> GetInvoiceUnitsFromSession() { HttpContext.Session.LoadAsync(); var sessionString = HttpContext.Session.GetString("invoiceUnit"); if (sessionString is not null) { return JsonSerializer.Deserialize<List<InvoiceUnit>>(sessionString); } return Enumerable.Empty<InvoiceUnit>().ToList(); }
Edit控制器相关代码
var sessionInvoices = GetInvoicesFromSession(); var sessionInvoiceUnits = GetInvoiceUnitsFromSession(); var invoiceFromDbSession = sessionInvoices.FirstOrDefault(i => i.Id == id); List<InvoiceUnit> invoiceUnitsForInvoiceSession = new List<InvoiceUnit>(); foreach (var invoiceUnit in sessionInvoiceUnits) { if (invoiceUnit.InvoiceId == id) { invoiceUnitsForInvoiceSession.Add(invoiceUnit); } } GenericModel sessionData = new GenericModel(invoiceUnitsForInvoiceSession, invoiceFromDbSession); return View(sessionData);
问题原因
- Session存储顺序错误:Create方法中处理未登录用户发票单元时,先将未添加当前单元的旧列表序列化存入Session,再把当前单元加入列表。循环结束后,Session中存储的永远是缺少最后一个单元的列表,若发票只有多个单元,最终仅能保留前n-1个;若逻辑存在其他触发场景,会导致仅首个单元被正确存储。
- 异步方法未等待:
GetInvoiceUnitsFromSession()中调用HttpContext.Session.LoadAsync()但未使用await,同步环境下会导致Session数据未正确加载,可能获取到旧数据。
修复方案
1. 修正Create方法的发票单元存储顺序
将添加单元到列表的操作放到存储Session之前,且优化为所有单元添加完成后统一存储,避免循环内重复操作:
// 未登录用户分支的发票单元处理逻辑 foreach(var invoiceUnit in invoice.InvoiceUnits) { invoiceUnit.Id = Guid.NewGuid(); invoiceUnit.InvoiceId = invoice.Id; // 先将单元加入列表 invoiceUnitsFromSession.Add(invoiceUnit); } // 所有单元添加完成后,统一序列化存储到Session HttpContext.Session.SetString("invoiceUnit", JsonSerializer.Serialize(invoiceUnitsFromSession));
2. 修复Session加载的异步调用问题
移除不必要的异步加载调用(ASP.NET Core中调用GetString时会自动加载Session),或改为异步方法并等待:
同步版本(推荐当前场景)
private List<InvoiceUnit> GetInvoiceUnitsFromSession() { var sessionString = HttpContext.Session.GetString("invoiceUnit"); if (!string.IsNullOrEmpty(sessionString)) { return JsonSerializer.Deserialize<List<InvoiceUnit>>(sessionString); } return new List<InvoiceUnit>(); }
异步版本(若控制器方法改为异步)
private async Task<List<InvoiceUnit>> GetInvoiceUnitsFromSessionAsync() { await HttpContext.Session.LoadAsync(); var sessionString = HttpContext.Session.GetString("invoiceUnit"); if (!string.IsNullOrEmpty(sessionString)) { return JsonSerializer.Deserialize<List<InvoiceUnit>>(sessionString); } return new List<InvoiceUnit>(); }
内容的提问来源于stack exchange,提问作者jenlee123
相关产品推荐
相关产品推荐

