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

EF Core偶发IServiceProvider ObjectDisposedException问题求助

偶发ObjectDisposedException(IServiceProvider已释放)问题解决

问题根源

这个偶发异常的核心原因是DbContextFactory与依赖服务的生命周期不匹配:你注册了Transient生命周期的DbContextFactory,但业务服务LoadPurchaseService是Scoped(Blazor Server中对应Circuit生命周期)。当Circuit的Scoped服务容器被回收时,若仍有异步操作在使用由Transient Factory创建的DbContext,EF Core内部依赖的IServiceProvider可能已被释放,从而抛出异常。

修复方案

方案1:对齐DbContextFactory的生命周期为Scoped

修改Program.cs中AddDbContextFactory的生命周期配置,与Blazor Server的Circuit生命周期保持一致:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContextFactory<MyDbContext>(options => options.UseSqlServer(
  builder.Configuration.GetConnectionString("db"),
  sqlServerOptions => sqlServerOptions.CommandTimeout(5)
), ServiceLifetime.Scoped); // 将Transient改为Scoped

builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<LoadDataPort, LoadPurchaseService>();
[...]

Scoped的Factory会绑定到当前Circuit的服务容器,避免异步操作中访问已释放的服务提供者。

方案2:合并数据库查询减少异步嵌套

当前代码中两次独立的数据库查询可以合并为并行执行,减少异步操作的嵌套层级,同时避免跨DbContext的生命周期冲突:

public class LoadPurchaseService : LoadDataPort
{  
  public async Task<List<Purchase>> GetAllPurchases()
  {
    using MyDbContext dbContext = contextFactory.CreateDbContext();
    // 并行发起两个查询请求
    var purchasesTask = dbContext.Purchase
                    .Include(purchase => purchase.Status)
                    .AsNoTracking()
                    .OrderByDescending(n => n.SomeId)
                    .AsSplitQuery()
                    .TagWithCallSite()
                    .TagWith("GetAllPurchases")
                    .ToListAsync();
    var categoriesTask = dbContext.PurchaseCategory.AsNoTracking()
                    .OrderBy(category => category.Id)
                    .ToListAsync();

    await Task.WhenAll(purchasesTask, categoriesTask);
    var purchases = purchasesTask.Result;
    var allCategories = categoriesTask.Result;

    // 关联采购与分类数据
    purchases.ForEach(purchase =>
    {
      purchase.Categories = purchase.CategoriesIds
        .Join(allCategories, catId => catId, cat => cat.Id, (_, cat) => cat)
        .ToList();
    });

    return purchases;
  }
}

这种方式不仅提升了查询性能,还消除了跨方法异步调用带来的生命周期风险。

方案3:同步服务与Factory的生命周期

如果需要保留Transient的DbContextFactory,可以将LoadPurchaseService也注册为Transient,确保每次服务调用都使用全新的实例:

builder.Services.AddTransient<LoadDataPort, LoadPurchaseService>();

避免Scoped服务长期持有Transient Factory实例,导致后续异步操作中访问已释放的依赖服务。

额外建议

  • Blazor Server中,Scoped服务绑定到Circuit生命周期,Transient服务则是每次请求创建,尽量避免在Scoped服务中持有Transient服务实例,尤其是涉及异步操作的场景。
  • 对于分类这类变更频率低、访问频率高的数据,可以通过IMemoryCache或IDistributedCache做内存缓存,减少数据库查询次数,同时从根源上避免此类生命周期问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 15:04:55