如何防止注入的DbContext被释放?遇ObjectDisposedException
我创建了ObjectBuilder类,通过构造函数注入dbContext并将其设为只读属性。类中包含多个使用dbContext的方法,比如CreateOrUpdateObject。在API控制器中实例化ObjectBuilder后,连续三次调用CreateOrUpdateObject方法:第一次执行正常,但第二次触发了ObjectDisposedException。我理解该异常含义,但未手动释放上下文或使用using语句,无法解决单次方法执行后上下文就被释放、后续方法无法运行的问题,恳请告知如何避免DbContext被释放以支持链式调用。
ObjectBuilder类代码片段
class ObjectBuilder { readonly GuidObjectBuilder _guidObjectBuilder; readonly projectviewerContext _db; bool UpdatedGuidsArray { get; set; } int UpdatedObjectCount { get; set; } int CreatedObjectCount { get; set; } ProjectObjects ActiveObject { get; set; } public ObjectBuilder(GuidObjectBuilder guidObjectBuilder, projectviewerContext db) { this._guidObjectBuilder = guidObjectBuilder; this._db = db; this.UpdatedGuidsArray = false; this.UpdatedObjectCount = 0; this.CreatedObjectCount = 0; } public async Task<dynamic> CreateOrUpdateObject(string projectName, string tag, string pidClass, string guId, string fileName) { //SEND ONE ENTRY RETURNS A PROJECT_OBJECT MODEL if (this._db == null) throw new ArgumentNullException("The object builder class has not been set a database. Set the object with a database"); try { //Check for an initial entry List<ProjectObjects> matchingProjectObjects = (from data in this._db.ProjectObjects.ToList() where data.ProjectName == projectName && data.Tag == tag select data).ToList(); if (matchingProjectObjects.Any()) { this.ActiveObject = this.UpdateProjectObject(matchingProjectObjects[0], pidClass, guId, fileName); this._db.Update(this.ActiveObject); this.UpdatedObjectCount++; } else { this.ActiveObject = this.CreateProjectObject(projectName, tag, pidClass, guId, fileName); this._db.Add(this.ActiveObject); this.CreatedObjectCount++; } await this._db.SaveChangesAsync(); if (this.UpdatedGuidsArray) { //Add new guid await this._guidObjectBuilder.CreateOrUpdateUponModelOpen(this.ActiveObject.Id, guId, fileName); } this.UpdatedGuidsArray = false; return this.ActiveObject; } catch (System.Exception error) { return error; } } }
测试端点代码片段
[HttpPost] [Route("api/objectCreation/test1")] public async Task<dynamic> TestOne() { try { projectviewerContext db = new projectviewerContext(); ObjectBuilder objectBuilder = new ObjectBuilder(new GuidObjectBuilder(db), db); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00011", "Gate Valve", "XXCCBBVVNNDDFFHH", "3D_Model.nwd"); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00012", "Gate Valve", "XXFFGGHHIILLMMNN", "3D_Model.nwd"); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00014", "Gate Valve", "123456789", "PID_drawing.nwd"); return "bang"; } catch (System.Exception error) { return error.Message; } }
问题原因及解决办法
核心原因
问题大概率出在GuidObjectBuilder内部:你将同一个dbContext实例传给了ObjectBuilder和GuidObjectBuilder,如果GuidObjectBuilder的CreateOrUpdateUponModelOpen方法里存在释放dbContext的逻辑(比如用using包裹传入的dbContext、手动调用db.Dispose()),就会导致后续调用时ObjectBuilder持有的dbContext已被销毁。
另外,手动实例化dbContext的方式不符合ASP.NET Core的生命周期管理规范,也容易引发此类问题。
解决步骤
检查并修复GuidObjectBuilder的实现
打开GuidObjectBuilder代码,确认CreateOrUpdateUponModelOpen方法中是否存在释放dbContext的逻辑:- 若有用
using(var db = ...)包裹传入的dbContext,直接删除该结构 - 若有调用
db.Dispose(),立即移除该代码
dbContext的生命周期应由外部(如依赖注入容器)统一管理,内部方法不应私自释放实例。
- 若有用
改用依赖注入管理DbContext生命周期(推荐)
放弃手动new dbContext,通过ASP.NET Core依赖注入容器统一管理对象生命周期:- 在
Program.cs中注册DbContext:builder.Services.AddDbContext<projectviewerContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString"))); - 注册
GuidObjectBuilder和ObjectBuilder为Scoped服务(与DbContext生命周期一致):builder.Services.AddScoped<GuidObjectBuilder>(); builder.Services.AddScoped<ObjectBuilder>(); - 修改控制器,通过构造函数注入
ObjectBuilder:private readonly ObjectBuilder _objectBuilder; public YourController(ObjectBuilder objectBuilder) { _objectBuilder = objectBuilder; } [HttpPost] [Route("api/objectCreation/test1")] public async Task<dynamic> TestOne() { try { await _objectBuilder.CreateOrUpdateObject("Project 1", "VV00011", "Gate Valve", "XXCCBBVVNNDDFFHH", "3D_Model.nwd"); await _objectBuilder.CreateOrUpdateObject("Project 1", "VV00012", "Gate Valve", "XXFFGGHHIILLMMNN", "3D_Model.nwd"); await _objectBuilder.CreateOrUpdateObject("Project 1", "VV00014", "Gate Valve", "123456789", "PID_drawing.nwd"); return "bang"; } catch (System.Exception error) { return error.Message; } }
依赖注入容器会自动确保在整个请求周期内,dbContext实例不会被提前释放。
- 在
临时应急方案(不推荐,仅用于快速验证)
若暂时无法修改依赖注入逻辑,可在测试端点中用using包裹dbContext实例,确保所有调用完成后才回收:[HttpPost] [Route("api/objectCreation/test1")] public async Task<dynamic> TestOne() { try { using(var db = new projectviewerContext()) { ObjectBuilder objectBuilder = new ObjectBuilder(new GuidObjectBuilder(db), db); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00011", "Gate Valve", "XXCCBBVVNNDDFFHH", "3D_Model.nwd"); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00012", "Gate Valve", "XXFFGGHHIILLMMNN", "3D_Model.nwd"); await objectBuilder.CreateOrUpdateObject("Project 1", "VV00014", "Gate Valve", "123456789", "PID_drawing.nwd"); } return "bang"; } catch (System.Exception error) { return error.Message; } }
内容的提问来源于stack exchange,提问作者SgDyl95

