ASP.NET Core MVC C# POST时如何保留下拉列表值避免重复查询数据库
解决方案
针对你遇到的POST提交后视图模型下拉集合丢失、需要重复查库的问题,有以下几种成熟的实现方案:
方案1:基础数据全局缓存(推荐)
你用到的联邦银行列表、州列表、状态列表、清算实体列表都属于变更频率极低的公共基础数据,完全可以通过服务端缓存存储,避免重复查询数据库。
你可以直接使用ASP.NET Core自带的IMemoryCache实现,示例修改如下:
// 先在Program.cs注册缓存服务 builder.Services.AddMemoryCache(); // 封装获取下拉数据的公共方法,自动走缓存 private async Task<(IEnumerable<C21FederalReserveBank> C21FedBanks, IEnumerable<FedBank> ACHFedBanks, IEnumerable<State> States, IEnumerable<ClearingEntity> ClearingEntities, IEnumerable<Status> Statuses)> GetDropDownDataAsync() { const string cacheKey = "BankDropDownDatas"; if (!_memoryCache.TryGetValue(cacheKey, out var dropDownData)) { var fedBanks = await _bankLookupService.GetFedBanksAsync(); dropDownData = new { C21FedBanks = fedBanks.Select(b => new C21FederalReserveBank { C21FederalReserveBankID = b.Id, Name = b.ValueName }), ACHFedBanks = fedBanks, States = await _bankLookupService.GetStatesAsync(), ClearingEntities = await _bankLookupService.GetClearingEntitiesAsync(), Statuses = await _bankLookupService.GetStatusesAsync() }; // 缓存12小时,可根据数据更新频率调整 _memoryCache.Set(cacheKey, dropDownData, TimeSpan.FromHours(12)); } return (dropDownData.C21FedBanks, dropDownData.ACHFedBanks, dropDownData.States, dropDownData.ClearingEntities, dropDownData.Statuses); }
之后不管是GET的页面加载还是POST校验失败返回视图,直接调用这个方法即可,只会在缓存失效时才查询一次数据库,所有用户共享缓存数据,性能最优。
方案2:TempData临时存储
如果这些下拉数据是和当前用户请求关联的专属数据,可以用TempData存储:
// GET请求加载页面时存入TempData var dropDownData = await GetDropDownDataAsync(); BankDetailViewModel vm = new BankDetailViewModel { BankDetails = await _bankService.GetBankAsync(id), C21FedBanks = dropDownData.C21FedBanks, ACHFedBanks = dropDownData.ACHFedBanks, States = dropDownData.States, ClearingEntities = dropDownData.ClearingEntities, Statuses = dropDownData.Statuses }; TempData["BankDropDownData"] = System.Text.Json.JsonSerializer.Serialize(dropDownData); TempData.Keep("BankDropDownData"); // 保留数据防止读取后被自动清除 // POST校验失败时直接从TempData读取,不用查库 if (!ModelState.IsValid) { var dropDownData = System.Text.Json.JsonSerializer.Deserialize<DropDownDataDto>(TempData["BankDropDownData"].ToString()); model.C21FedBanks = dropDownData.C21FedBanks; model.ACHFedBanks = dropDownData.ACHFedBanks; model.States = dropDownData.States; model.Statuses = dropDownData.Statuses; model.ClearingEntities = dropDownData.ClearingEntities; TempData.Keep("BankDropDownData"); return View("BankDetails", model); }
TempData默认是单次请求有效,调用Keep方法可以延长数据有效期到下一次请求。
方案3:前端隐藏域序列化
如果不想占用服务端存储资源,可以把下拉集合序列化为JSON存在视图的隐藏域中,表单提交时自动带回服务端:
<!-- 在cshtml视图中添加隐藏域 --> <input type="hidden" name="C21FedBanksJson" value="@System.Text.Json.JsonSerializer.Serialize(Model.C21FedBanks)" /> <input type="hidden" name="ACHFedBanksJson" value="@System.Text.Json.JsonSerializer.Serialize(Model.ACHFedBanks)" /> <!-- 其他集合同理 -->
POST方法中直接从表单参数读取反序列化即可:
if (!ModelState.IsValid) { model.C21FedBanks = System.Text.Json.JsonSerializer.Deserialize<List<C21FederalReserveBank>>(Request.Form["C21FedBanksJson"]); model.ACHFedBanks = System.Text.Json.JsonSerializer.Deserialize<List<FedBank>>(Request.Form["ACHFedBanksJson"]); // 其他集合同理 return View("BankDetails", model); }
注意该方案适合集合数据量较小的场景,数据量过大会增加表单提交的体积和耗时,另外建议添加哈希校验防止数据被篡改。
内容的提问来源于stack exchange,提问作者Cef
相关产品推荐
相关产品推荐

