Razor视图点击详情按钮报对象引用未实例化错误排查解决
错误根因
触发空引用错误的核心原因非常明确:你在控制器查询逻辑中硬编码了r.Validated == 2的过滤规则,当目标记录的Validated值为1时,查询返回的results是空集合。此时在视图中直接调用Model.FirstOrDefault()会返回null,后续直接访问null对象的program_id、Validated属性必然抛出object reference not set to an instance of an object错误。
必查排查点
- 在控制器Details Action中打调试断点,查看查询执行完成后
results集合的Count属性:只要Count为0,FirstOrDefault()返回值就是null,直接访问属性一定会报错 - 检查查询条件是否存在逻辑冲突:现有代码写了两次
r.custid判断,一次匹配当前登录UserId、一次匹配传入的custId,要确认报错场景下两个值是否一致,避免条件矛盾导致查不出数据 - 检查关联导航属性加载情况:就算主集合有数据,如果
sample、Program、LabTest、Machine、performance这些关联实体没有做预加载,视图中访问这些导航属性的字段时同样会触发空引用错误
需求实现方案
要实现「Validated=2时正常加载展示报告,Validated=1时显示无数据空视图」的效果,不要在控制器层硬过滤Validated=2,把判断逻辑下沉到视图层,全程做好空值防护即可。
第一步:修改控制器代码
去掉r.Validated == 2的硬过滤,添加关联表预加载逻辑,避免导航属性空引用:
public ActionResult Details(int id , int programId, int custId , int MachineId) { int UserId = Convert.ToInt32(Session["UserID"]); var results = _context.RESULTS .Include(r=>r.sample) .Include(r=>r.Program) .Include(r=>r.LabTest) .Include(r=>r.Machine) .Include(r=>r.performance) .Where(r => r.custid == UserId && r.sample_id == id && r.status == 2 && r.program_id == programId && r.custid == custId && MachineId == r.machine_id) .ToList(); return View(results); }
第二步:修改视图代码
先统一取集合首项做非空判断,再走分支逻辑,避免重复调用FirstOrDefault():
@model IEnumerable<warehouse.Models.RESULT> @{ ViewBag.Title = "Details"; Layout = "~/Views/Shared/_LayoutDashboard.cshtml"; var firstItem = Model.FirstOrDefault(); } @* 无数据或记录未审核时,显示空提示 *@ @if (firstItem == null || firstItem.Validated != 2) { <div style="margin:50px;text-align:center;"> <p>当前记录未完成审核,暂无报告数据</p> @Html.ActionLink("返回列表", "Indexs", "HospitalSamples") </div> } @* 审核通过且program_id小于6时,展示第一类报告模板 *@ else if (firstItem.program_id < 6) { <img style="margin-left:250px;" src="~/images/weblogo.png" /> <div style="margin-left:50px;"> @Html.ActionLink("返回列表", "Indexs", "HospitalSamples") </div> <table class="table"> <tr style="background-color:hotpink;text-align:left"> <th>@Html.DisplayNameFor(model => model.sample.name)</th> <th>@Html.DisplayNameFor(model => model.Program.name)</th> <th>@Html.DisplayNameFor(model => model.LabTest.TestName)</th> <th>@Html.DisplayNameFor(model => model.APPROVED_DATE)</th> <th>@Html.DisplayNameFor(model => model.RESULT1)</th> <th>@Html.DisplayNameFor(model => model.mean)</th> <th>@Html.DisplayNameFor(model => model.sd)</th> <th>@Html.DisplayNameFor(model => model.sdi)</th> <th>@Html.DisplayNameFor(model => model.low_limit)</th> <th>@Html.DisplayNameFor(model => model.high_limit)</th> <th>@Html.DisplayNameFor(model => model.Machine.Machine_name)</th> <th>@Html.DisplayNameFor(model => model.performance.name)</th> </tr> @foreach (var item in Model) { <tr> <td>@Html.DisplayFor(modelItem => item.sample.name)</td> <td>@Html.DisplayFor(modelItem => item.Program.name)</td> <td>@Html.DisplayFor(modelItem => item.LabTest.TestName)</td> <td>@Html.DisplayFor(modelItem => item.APPROVED_DATE)</td> <td>@Html.DisplayFor(modelItem => item.RESULT1)</td> <td>@Html.DisplayFor(modelItem => item.mean)</td> <td>@Html.DisplayFor(modelItem => item.sd)</td> <td>@Html.DisplayFor(modelItem => item.sdi)</td> <td>@Html.DisplayFor(modelItem => item.low_limit)</td> <td>@Html.DisplayFor(modelItem => item.high_limit)</td> <td>@Html.DisplayFor(modelItem => item.Machine.Machine_name)</td> <td>@Html.DisplayFor(modelItem => item.performance.name)</td> </tr> } </table> } @* 审核通过且program_id等于6时,展示第二类报告模板 *@ else if (firstItem.program_id == 6) { <img style="margin-left:250px;" src="~/images/weblogo.png" /> <div style="margin-left:50px;"> @Html.ActionLink("返回列表", "Indexs", "HospitalSamples") </div> <table class="table"> <tr style="background-color:hotpink;text-align:left"> <th>@Html.DisplayNameFor(model => model.sample.name)</th> <th>@Html.DisplayNameFor(model => model.Program.name)</th> <th>@Html.DisplayNameFor(model => model.LabTest.TestName)</th> <th>@Html.DisplayNameFor(model => model.APPROVED_DATE)</th> <th>@Html.DisplayNameFor(model => model.RESULT1)</th> <th>@Html.DisplayNameFor(model => model.accepted_BG)</th> <th>@Html.DisplayNameFor(model => model.Machine.Machine_name)</th> <th>@Html.DisplayNameFor(model => model.performance.name)</th> </tr> @foreach (var item in Model) { <tr> <td>@Html.DisplayFor(modelItem => item.sample.name)</td> <td>@Html.DisplayFor(modelItem => item.Program.name)</td> <td>@Html.DisplayFor(modelItem => item.LabTest.TestName)</td> <td>@Html.DisplayFor(modelItem => item.APPROVED_DATE)</td> <td>@Html.DisplayFor(modelItem => item.RESULT1)</td> <td>@Html.DisplayFor(modelItem => item.accepted_BG)</td> <td>@Html.DisplayFor(modelItem => item.Machine.Machine_name)</td> <td>@Html.DisplayFor(modelItem => item.performance.name)</td> </tr> } </table> }
优化注意事项
- 不要在视图中多次调用
FirstOrDefault(),统一取值存为变量即可,既减少重复执行逻辑,也方便统一做非空防护 - 如果使用EF查询数据,一定要通过
Include方法预加载所有视图中用到的导航关联属性,否则就算主记录存在,关联表数据未加载时访问导航属性也会抛出空引用错误 - 原控制器查询中两次判断
r.custid的逻辑建议确认业务合理性:如果要求custid同时匹配当前登录用户ID和传入的custId,要保证列表页跳转时传入的custId和当前登录用户ID一致,否则会出现有权限的用户也查不到数据的问题
内容的提问来源于stack exchange,提问作者MarwanAbu
相关产品推荐
相关产品推荐

