You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:14:07