数据注解验证中断逻辑致LiteDB服务无法释放问题求助
我有一个注册为Transient的VendaService,用来处理LiteDB的持久化逻辑。每次调用服务时需要读取或创建LiteDB文件,而LiteDB必须手动调用Dispose才能释放资源。
当前代码里,VendaService在构造函数中初始化LiteDatabase,但服务是在控制器构造时注入的——当请求的模型数据注解验证失败时,控制器方法根本不会执行,导致_dbContext.Dispose()没机会被调用,LiteDB资源无法释放,直接阻塞后续请求。
服务代码:
public class VendaService : IVendaService<LiteDatabase> { public readonly LiteDatabase _dbContext; public readonly ILiteCollection<Venda> collection; public VendaService() { // 读取或创建数据库文件 _dbContext = new LiteDatabase(...); // 获取或创建集合 collection = _dbContext.GetCollection<Venda>("vendas"); // 无数据时初始化样本数据 if(collection.Count() == 0){...}; } public async Task<Venda> PostVendaService(Vendedor vendedor, List<Item> carrinho) { Venda doc = new Venda() {...}; var collection = _dbContext.GetCollection<Venda>("vendas"); collection.Insert(doc); _dbContext.Dispose(); return doc; } }
控制器相关代码:
[HttpPost("api")] [ProducesResponseType(StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] public async Task<IActionResult> PostVenda(PostVendaRequestModel postRequest) { if (postRequest.carrinho.Count == 0){...} foreach (Item item in postRequest.carrinho){...} Venda postVenda = await _vendaService.PostVendaService( vendedor: postRequest.vendedor, carrinho: postRequest.carrinho); return CreatedAtAction(nameof(GetVendaById), new { id = postVenda.VendaId.ToString() }, postVenda); } [BindProperties] public class PostVendaRequestModel { [Required] public Vendedor vendedor { get; set; } [Required] public List<Item> carrinho { get; set; } }
补充:试过在构造函数里调用Dispose也没用,只要模型验证失败,资源释放逻辑就触发不了。
1. 让VendaService实现IDisposable接口
既然服务是Transient生命周期,ASP.NET Core的DI容器会在请求结束时自动调用实现了IDisposable的服务的Dispose方法——不管控制器方法有没有执行。这样哪怕模型验证失败,容器也会负责释放LiteDB资源。
修改后的VendaService:
public class VendaService : IVendaService<LiteDatabase>, IDisposable { private readonly LiteDatabase _dbContext; public readonly ILiteCollection<Venda> collection; private bool _disposed = false; public VendaService() { _dbContext = new LiteDatabase(...); collection = _dbContext.GetCollection<Venda>("vendas"); if(collection.Count() == 0){...}; } public async Task<Venda> PostVendaService(Vendedor vendedor, List<Item> carrinho) { Venda doc = new Venda() {...}; collection.Insert(doc); return doc; } // 实现IDisposable接口 public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 释放LiteDB托管资源 _dbContext?.Dispose(); } _disposed = true; } // 析构函数,防止托管资源未被正常释放 ~VendaService() { Dispose(false); } }
现在不用在PostVendaService里手动调用_dbContext.Dispose()了,容器会在请求结束时自动处理。
2. 将LiteDatabase的创建移到方法内部,用using包裹
如果不想让服务实现IDisposable,可以把数据库实例的创建放到业务方法里,用using语句自动释放资源。同时把初始化样本数据的逻辑调整到方法内(比如加个静态标记确保只执行一次)。
修改后的服务代码:
public class VendaService : IVendaService<LiteDatabase> { private readonly string _dbPath; // 注入数据库路径,而非在构造函数创建实例 private static bool _sampleDataInitialized = false; public VendaService(string dbPath) { _dbPath = dbPath; } public async Task<Venda> PostVendaService(Vendedor vendedor, List<Item> carrinho) { using(var dbContext = new LiteDatabase(_dbPath)) { var collection = dbContext.GetCollection<Venda>("vendas"); // 只初始化一次样本数据 if(!_sampleDataInitialized && collection.Count() == 0) { // 初始化逻辑... _sampleDataInitialized = true; } Venda doc = new Venda() {...}; collection.Insert(doc); return doc; } // using块结束自动调用dbContext.Dispose() } }
这种方式下,不管方法是否正常执行,using块都会保证资源被释放,而且不需要依赖DI容器的Dispose逻辑。
3. 使用工厂模式延迟创建服务
如果不想在控制器构造时就创建VendaService,可以用Func<IVendaService<LiteDatabase>>委托延迟创建服务——直到控制器方法通过模型验证后再实例化服务。
修改控制器代码:
private readonly Func<IVendaService<LiteDatabase>> _vendaServiceFactory; // 注入工厂委托 public YourController(Func<IVendaService<LiteDatabase>> vendaServiceFactory) { _vendaServiceFactory = vendaServiceFactory; } [HttpPost("api")] public async Task<IActionResult> PostVenda(PostVendaRequestModel postRequest) { // 先检查模型验证状态 if (!ModelState.IsValid) { return BadRequest(ModelState); } // 验证通过后再创建服务,并用using自动释放(需要服务实现IDisposable) using(var vendaService = _vendaServiceFactory()) { if (postRequest.carrinho.Count == 0){...} foreach (Item item in postRequest.carrinho){...} Venda postVenda = await vendaService.PostVendaService( vendedor: postRequest.vendedor, carrinho: postRequest.carrinho); return CreatedAtAction(nameof(GetVendaById), new { id = postVenda.VendaId.ToString() }, postVenda); } }
这种方式从根源上避免了模型验证失败时创建服务,自然不会产生资源泄漏问题。
内容的提问来源于stack exchange,提问作者Yuri Soares

