.NET Core Razor Pages单页提交保存发票表头及明细行报错
解决发票创建页面的ArgumentNullException空引用异常
问题现状
开发发票创建页面时,需求是填写发票表头(InvHeader)和动态明细行(InvRow)后单次提交保存。已解决多行添加的RowList错误,但现在触发ArgumentNullException:值不能为 null。(参数 'source'),错误触发点为视图代码@for (int i = 0; i < Model.RowList.Count(); i++),根源是RowList为null时调用Count()方法导致空引用。
问题原因
- 后端PageModel中
RowList仅声明未初始化默认值,若OnGetAsync未执行(比如Post验证失败返回Page时),RowList会处于null状态。 OnPostAsync方法中,当Model验证失败返回Page时,未重新初始化RowList,导致视图渲染时Model.RowList为null,调用Count()抛出异常。
解决方案
后端代码修改
1. 初始化RowList默认值
修改PageModel属性声明,给RowList初始化空列表,从根源避免null:
[BindProperty] public InvHeader InvHeader { get; set; } = default!; // 初始化空列表,防止null public IList<InvRow> RowList { get; set; } = new List<InvRow>();
2. 完善OnPostAsync方法
当Model验证失败返回Page时,确保RowList有有效值,同时添加[BindProperty]保证表单数据正确绑定:
public async Task<IActionResult> OnPostAsync() { if (!ModelState.IsValid) { // 若提交的RowList为空,保持空列表而非null RowList = RowList ?? new List<InvRow>(); return Page(); } _context.InvHeaders.Add(InvHeader); await _context.InvRows.AddRangeAsync(RowList); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); }
注:移除原方法的
IList<InvRow> RowList参数,改用[BindProperty]绑定,避免参数传递导致的null问题。
3. 保持OnGetAsync原有逻辑(可选)
原有初始化默认行的逻辑无需修改,初始化默认值后即使不赋值,RowList也会是安全的空列表:
public IActionResult OnGetAsync() { var rr = new List<InvRow>() { new InvRow() { Name = "Apple", Qty = 5, Price = 100 }, new InvRow() { Name = "Peach", Qty = 3, Price = 500 }, new InvRow() { Name = "Ananas", Qty = 1, Price = 1100 }, }; RowList = rr; return Page(); }
前端代码修改
统一数据源为RowList,并添加null判断避免渲染异常:
<form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <!-- 表头表格 --> <table class="table table-striped border-0" style="width: 100%; text-align: left;"> <thead class="border-0"> <tr class="border-0"> <td class="border-0" style="min-width:100%;"> <div class="border border-secondary p-4"> <div class="row"> <div style="float:left;width:50%;"> <div class="form-group m-1"> <label asp-for="InvHeader.Cli" class="control-label" style="font-size:80%;">Client</label> <input asp-for="InvHeader.Cli" class="form-control" value="clients name" /> </div> </div> <div style="float:left;width:25%;"> <div class="form-group m-1"> <input asp-for="InvHeader.InvDate" class="form-control" type="date" value="@(DateTime.UtcNow.Date.ToString("yyyy-MM-dd"))" /> <span asp-validation-for="InvHeader.InvDate" class="text-danger"></span> </div> </div> <div style="float:left;width:25%;"> <div class="form-group m-1"> <input asp-for="InvHeader.InvNr" class="form-control" value="33" /> <span asp-validation-for="InvHeader.InvNr" class="text-danger"></span> </div> </div> </div> </div> </td> </tr> </thead> </table> <!-- 动态明细行表格 --> <table id="table1" border="0"> <tr style="font-size:80%"> <th style="width:50%">Product</th> <th style="width:5%">qty</th> <th style="width:5%">price</th> </tr> <!-- 添加null判断,安全遍历RowList --> @if (Model.RowList != null) { @for (int i = 0; i < Model.RowList.Count(); i++) { <tr class="border-bottom"> <td> <select asp-for="RowList[i].ProdId" class="form-control" style="width:100%"> <!-- 替换为实际产品数据源,示例用Model.Products --> @foreach (var product in Model.Products) { <option value="@product.ProdId" price="@product.Price">@product.Name, @product.Price</option> } </select> </td> <td><input asp-for="RowList[i].Qty" class="form-control" style="width:100%" /></td> <td><input asp-for="RowList[i].Price" class="form-control" style="width:100%" /></td> </tr> } } </table> </form>
注:原前端代码中
Model.PL、Model.MR与后端RowList不匹配,需统一为RowList保证表单绑定生效。
内容的提问来源于stack exchange,提问作者Fillo
相关产品推荐
相关产品推荐

