在 ASP.NET Core MVC 中用 select 下拉框动态更新 dbo.Assets.State
实现方式
核心是新增一个只更新资产状态的 POST 接口,下拉框变化时通过 fetch 调用该接口,不需要刷新整个页面。
当前视图还有一个问题:每行下拉框都使用相同的 id="assetStateDropdown",而内联脚本总是通过 getElementById 获取第一个元素。应改为使用 class 和 data-* 属性。
1. 新增请求模型
public class UpdateAssetStateRequest { public AssetState State { get; set; } }
2. 在 AssetsController 中添加更新操作
假设 DbContext 已经通过构造函数注入,变量名为 _context。
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> UpdateState( int id, [FromBody] UpdateAssetStateRequest request) { if (request is null || !ModelState.IsValid) { return BadRequest(); } // 阻止传入未定义的枚举值 if (!Enum.IsDefined(typeof(AssetState), request.State)) { return BadRequest(); } // 删除状态应通过单独的删除操作处理 if (request.State == AssetState.deleted) { return BadRequest(); } var asset = await _context.Assets.FindAsync(id); if (asset is null) { return NotFound(); } // 已删除资产不允许通过该接口恢复 if (asset.State == AssetState.deleted) { return BadRequest(); } asset.State = request.State; await _context.SaveChangesAsync(); return Json(new { success = true, state = (int)asset.State }); }
EF Core 默认会把枚举映射为整数,因此这里会直接更新 dbo.Assets.State 列。
3. 修改视图
在表格前面添加防伪令牌:
@Html.AntiForgeryToken()
然后将原来的状态列替换为下面的内容:
<td> @if (asset.State != Models.AssetState.deleted) { <select class="dropdown asset-state-select" data-asset-id="@asset.Id" data-url="@Url.Action("UpdateState", "Assets", new { id = asset.Id })" data-current-state="@((int)asset.State)"> <option value="@((int)Models.AssetState.functional)" selected="@(asset.State == Models.AssetState.functional)"> Functional </option> <option value="@((int)Models.AssetState.non_functional)" selected="@(asset.State == Models.AssetState.non_functional)"> Non-functional </option> <option value="@((int)Models.AssetState.under_maintenance)" selected="@(asset.State == Models.AssetState.under_maintenance)"> Under-Maintenance </option> </select> } else { <p>Deleted</p> } </td>
这里不再输出重复的 id,也不需要每个下拉框后面跟一段内联脚本。
4. 添加 JavaScript 提交逻辑
把下面代码放到视图底部的 Scripts 区块中:
@section Scripts { <script> document.addEventListener('change', async function (e) { const select = e.target.closest('.asset-state-select'); if (!select) { return; } const token = document.querySelector( 'input[name="__RequestVerificationToken"]' )?.value; const originalValue = select.dataset.currentState; select.disabled = true; try { const response = await fetch(select.dataset.url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'RequestVerificationToken': token }, body: JSON.stringify({ state: Number(select.value) }) }); if (!response.ok) { throw new Error('状态更新失败'); } const data = await response.json(); select.dataset.currentState = String(data.state); } catch { // 保存失败时恢复下拉框原来的值 select.value = originalValue; alert('状态保存失败,请稍后重试。'); } finally { select.disabled = false; } }); </script> }
如果布局页没有定义 Scripts 区块,直接把这段 <script> 放在表格后面即可。
最终答案
给每个下拉框添加 data-url 和 data-current-state,使用一个委托的 change 事件通过 fetch 发起 POST 请求;服务端验证枚举值和资产状态后,用 EF Core 修改 State 并调用 SaveChangesAsync。保存成功后更新当前值,失败时将下拉框恢复为原状态。
内容的提问来源于stack exchange,提问作者kesarling
相关产品推荐
相关产品推荐

