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实例:
- 先在
Program.cs注册DbContextFactory:
builder.Services.AddDbContextFactory<Senario01DbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
- 修改过滤器:
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"); } }
- 同样在
Program.cs中直接注册过滤器即可,无需手动传参。
补充说明
你之前尝试仓储模式无效的原因:如果还是在Program.cs的using块中获取仓储实例传给过滤器,仓储内部持有的DbContext依然会随着作用域释放而被销毁,核心问题是过滤器生命周期与DbContext生命周期不匹配,而非是否使用仓储。
内容的提问来源于stack exchange,提问作者ari23yan
相关产品推荐
相关产品推荐

