ASP.NET Core MVC中如何将IQueryable转换为ViewModel列表?
问题分析
你遇到的核心问题是控制器返回的是IQueryable<ApplicantViewModel>类型的查询对象,而视图期望的是单个ApplicantViewModel实例。因为Details页面是用来展示单条申请人的详情,而不是列表,所以直接传递未执行的查询是无法让视图正确绑定数据的。
另外还有一个潜在问题:你的视图同时渲染了personapplicantvm和legalapplicantvm的字段,但根据申请人类型,其中一个必然是null,直接访问属性会导致空引用异常,这点也需要处理。
解决方案
1. 修正控制器代码
修改Details方法,执行查询并获取单个ApplicantViewModel实例,而不是返回IQueryable:
public async Task<IActionResult> Details(int? id) { if (id == null) { return NotFound(); } // 先获取基础申请人信息,同时判断是否存在 var applicant = await _context.Applicant .Include(a => a.ApplicantTypeNavigation) .FirstOrDefaultAsync(m => m.ApplicantId == id); if (applicant == null) { return NotFound(); } ApplicantViewModel viewModel = null; if (applicant.ApplicantType == 1) { // 针对当前ID查询个人申请人的视图模型,直接获取单个实例 viewModel = await (from t1 in _context.Applicant join t2 in _context.PersonApplicant on new { t1.ApplicantId, t1.ApplicantType } equals new { t2.ApplicantId, t2.ApplicantType } where t1.ApplicantId == id select new ApplicantViewModel { applicantvm = t1, personapplicantvm = t2 }) .FirstOrDefaultAsync(); } else if (applicant.ApplicantType == 2) { // 针对当前ID查询法人申请人的视图模型,直接获取单个实例 viewModel = await (from t1 in _context.Applicant join t2 in _context.LegalApplicant on new { t1.ApplicantId, t1.ApplicantType } equals new { t2.ApplicantId, t2.ApplicantType } where t1.ApplicantId == id select new ApplicantViewModel { applicantvm = t1, legalapplicantvm = t2 }) .FirstOrDefaultAsync(); } if (viewModel == null) { return NotFound(); } return View(viewModel); }
关键修改点:
- 移除了全局的
IQueryable变量,直接针对当前id执行查询并获取单个实体 - 添加
where t1.ApplicantId == id过滤条件,确保只查询当前申请人的关联数据 - 使用
FirstOrDefaultAsync()执行查询,将IQueryable转换为单个ApplicantViewModel实例 - 增加多处
null判断,避免空引用异常
2. 修正视图代码
因为视图中同时渲染了个人和法人的字段,需要根据ApplicantType判断显示对应的部分,避免空引用:
@model CSDDashboard.Models.ApplicantViewModel @{ ViewData["Title"] = "Details"; } <h1>Details</h1> <div> <h4>Applicant </h4> <hr /> <dl class="row"> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.applicantvm.ApplicantType) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.applicantvm.ApplicantType) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.applicantvm.Name) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.applicantvm.Name) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.applicantvm.Address) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.applicantvm.Address) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.applicantvm.Description) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.applicantvm.Description) </dd> <!-- 根据申请人类型渲染对应字段 --> @if (Model.applicantvm.ApplicantType == 1) { <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.personapplicantvm.NationalCode) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.personapplicantvm.NationalCode) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.personapplicantvm.Username) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.personapplicantvm.Username) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.personapplicantvm.IssuePlace) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.personapplicantvm.IssuePlace) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.personapplicantvm.BirthCertificateNo) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.personapplicantvm.BirthCertificateNo) </dd> } else if (Model.applicantvm.ApplicantType == 2) { <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.legalapplicantvm.NationalCode) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.legalapplicantvm.NationalCode) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.legalapplicantvm.RegisterNo) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.legalapplicantvm.RegisterNo) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.legalapplicantvm.EconomicCode) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.legalapplicantvm.EconomicCode) </dd> } </dl> </div> <div> <a asp-action="Edit" asp-route-id="@Model.applicantvm.ApplicantId">Edit</a> | <a asp-action="Index">Back to list</a> </div>
视图修改点:
- 补充了
<dd>标签来显示字段值(原视图仅定义了标题,没有展示实际数据) - 使用
@if分支判断申请人类型,只渲染对应类型的字段,避免访问null对象的属性 - 修复了"Back to list"的跳转链接,添加
asp-action="Index"确保正确返回列表页
为什么原来的代码不工作?
IQueryable本质是一个查询的定义,它并没有实际执行数据库查询,也不是单个实体对象。而你的视图明确声明了需要ApplicantViewModel类型的单个实例,所以无法直接绑定IQueryable类型的数据源。通过调用FirstOrDefaultAsync(),我们触发了数据库查询并获取了单个实体,这样视图才能正确读取并渲染数据。
内容的提问来源于stack exchange,提问作者hrz
相关产品推荐
相关产品推荐

