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

System.ObjectDisposedException异常:Swagger过滤器中DbContext已释放问题

异常原因分析

1. DbContext生命周期不匹配(核心问题)

你在Program.cs的using块中创建了服务作用域,取出Senario01DbContext后直接传给OperationFilter。当using块结束时,作用域会被释放,内部的DbContext也会被自动Dispose。而Swagger的OperationFilter是单例生命周期,后续Swagger生成文档调用Apply方法时,持有的DbContext已经被释放,就会触发ObjectDisposedException。

2. 异步方法误用(次要问题)

Apply方法使用了async void,这是C#异步编程的错误写法:异步操作会脱离当前上下文执行,无法保证DbContext在操作完成前不被释放,还会导致无法捕获异步异常。


解决方案

方案一:通过IServiceProvider临时获取DbContext(通用兼容方案)

修改过滤器,注入IServiceProvider,在Apply方法内临时创建作用域获取DbContext,用完自动释放:

public class AddCitiesToUserModelFilter : IOperationFilter
{
    private readonly IServiceProvider _serviceProvider; 

    public AddCitiesToUserModelFilter(IServiceProvider serviceProvider) 
    { 
        _serviceProvider = serviceProvider;
    }

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (IsAddUserEndpoint(context))
        {
            using var scope = _serviceProvider.CreateScope();
            var dbContext = scope.ServiceProvider.GetRequiredService<Senario01DbContext>();
            
            // 修正:获取城市列表应该用Cities表,不是Customers表
            var cities = dbContext.Cities.ToList();

            var cityEnumSchema = new OpenApiSchema
            {
                Type = "integer",
                Enum = cities.Select(city => new OpenApiInteger(city.Id) as IOpenApiAny).ToList(),
            };

            operation.RequestBody.Content["application/json"].Schema.Properties["selectedCityId"] = cityEnumSchema;
        }
    }

    private bool IsAddUserEndpoint(OperationFilterContext context)
    {
        var httpMethod = context.ApiDescription.HttpMethod;
        var relativePath = context.ApiDescription.RelativePath;

        return httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) &&
               relativePath.Contains("api/Home/AddUser");
    }
}

修改Program.cs的Swagger注册,让DI自动注入依赖,不再手动创建作用域:

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Your API", Version = "v1" });
    // 直接注册过滤器,无需手动传参
    c.OperationFilter<AddCitiesToUserModelFilter>();
});

方案二:使用EF Core的IDbContextFactory(推荐,符合EF最佳实践)

如果使用EF Core 5及以上版本,推荐注册IDbContextFactory来创建独立的DbContext实例:

  1. 先在Program.cs注册DbContextFactory:
builder.Services.AddDbContextFactory<Senario01DbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
  1. 修改过滤器:
public class AddCitiesToUserModelFilter : IOperationFilter
{
    private readonly IDbContextFactory<Senario01DbContext> _contextFactory; 

    public AddCitiesToUserModelFilter(IDbContextFactory<Senario01DbContext> contextFactory) 
    { 
        _contextFactory = contextFactory;
    }

    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (IsAddUserEndpoint(context))
        {
            using var dbContext = _contextFactory.CreateDbContext();
            var cities = dbContext.Cities.ToList();

            var cityEnumSchema = new OpenApiSchema
            {
                Type = "integer",
                Enum = cities.Select(city => new OpenApiInteger(city.Id) as IOpenApiAny).ToList(),
            };

            operation.RequestBody.Content["application/json"].Schema.Properties["selectedCityId"] = cityEnumSchema;
        }
    }

    private bool IsAddUserEndpoint(OperationFilterContext context)
    {
        var httpMethod = context.ApiDescription.HttpMethod;
        var relativePath = context.ApiDescription.RelativePath;

        return httpMethod.Equals("POST", StringComparison.OrdinalIgnoreCase) &&
               relativePath.Contains("api/Home/AddUser");
    }
}
  1. 同样在Program.cs中直接注册过滤器即可,无需手动传参。

补充说明

你之前尝试仓储模式无效的原因:如果还是在Program.cs的using块中获取仓储实例传给过滤器,仓储内部持有的DbContext依然会随着作用域释放而被销毁,核心问题是过滤器生命周期与DbContext生命周期不匹配,而非是否使用仓储。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:57:02