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

Blazor Server异步调用EF Core抛出NullReferenceException但同步调用正常

问题根因

这个问题是Blazor Server的预渲染机制 + EF Core 上下文生命周期不匹配共同导致的:

  1. 从你提供的异常堆栈可以看到,你的组件使用了ServerPrerendered渲染模式,该模式下同一个组件会执行两次初始化流程:
    • 第一次是HTTP请求触发的服务端预渲染,目的是生成静态HTML直接返回给浏览器,提升首屏加载速度和SEO效果,这个阶段使用的是普通HTTP请求的Scope
    • 第二次是浏览器加载完Blazor JS、建立SignalR连接后,组件再次初始化以建立交互式运行上下文,这个阶段使用的是SignalR连接绑定的Scope
  2. 你直接注入了Scoped生命周期的ApplicationDbContext,而EF Core的DbContext不是线程安全的,且Scope销毁时会自动释放DbContext:
    • 同步写法ToList()执行时,预渲染的整个流程在同一个线程内同步完成,查询 + 页面渲染全部执行完后HTTP请求的Scope才会销毁,DbContext在渲染阶段仍然可用,所以不会报错
    • 异步写法ToListAsync()执行时,await操作会让出当前线程,Blazor的预渲染逻辑可能在异步查询完成后、页面渲染前就销毁了HTTP请求的Scope,此时DbContext已经被释放。如果你在Razor模板中访问了User实体的懒加载导航属性、或者存在其他依赖DbContext的渲染逻辑,就会触发空引用异常
  3. 你断点调试时看到Context不为空、查询结果正常,是因为调试器默认捕获的是第二次SignalR阶段的初始化流程,第一次预渲染阶段的异常没有被你的断点命中。

解决方案

方案1(官方推荐):使用IDbContextFactory创建DbContext

这是微软官方推荐的Blazor Server对接EF Core的方案,可以完全避免生命周期不匹配问题:

  1. 先在Program.cs/Startup.cs中注册DbContext工厂:
services.AddDbContextFactory<ApplicationDbContext>(options => 
    options.UseSqlServer(/* 你的数据库连接配置 */));
  1. 组件内注入工厂,每次查询时手动创建和释放DbContext:
[Inject]
private IDbContextFactory<ApplicationDbContext> ContextFactory { get; set; }

private List<User> _feeEarners = new();

protected override async Task OnInitializedAsync()
{
    using var context = ContextFactory.CreateDbContext();
    // 如果用到导航属性,提前用Include预加载,避免懒加载问题
    _feeEarners = await context.Users.ToListAsync();
}

方案2:关闭预渲染

如果不需要首屏SEO和快速首屏展示,可以直接修改_Host.cshtml中的组件渲染模式,关闭预渲染即可解决问题:

<component type="typeof(App)" render-mode="Server" />

方案3:补充空安全判断

检查你的Razor模板中所有访问_feeEarners或User实体属性的代码,补充空安全操作符?.,避免渲染时空引用:

@* 示例 *@
@foreach (var user in _feeEarners)
{
    <div>@user?.Name</div>
}

你之前使用相同写法没有报错,大概率是之前的组件没有开启预渲染、或者没有在渲染阶段访问需要懒加载的导航属性,所以没有触发这个边界问题。

内容的提问来源于stack exchange,提问作者Avrohom Yisroel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 00:06:05