ASP.NET插入日志后抛UserFriendlyException时日志未写入数据库问题
问题:ASP.NET中插入日志后抛出异常,日志未写入数据库
我希望在ASP.NET中先将操作日志插入数据库,再抛出UserFriendlyException返回给用户,但实际执行不符合预期。即使调用了CurrentUnitOfWork.SaveChangesAsync(),系统仅抛出异常但日志未插入数据库。预期是日志成功写入数据库,同时向用户抛出UserFriendlyException。
代码示例
if (isTodayOpen) //today open but time not valid { var logMessage = string.Format("The door cannot be unlocked. Your booking is not yet in effect. It is valid from {0} to {1}. Please try again later.", application.Rules.TimeFrom.ToString(), application.Rules.TimeTo.ToString()); var openDoorLogDetails = new OpenDoorLog(); openDoorLogDetails.ApplicantId = application.User.Id; openDoorLogDetails.OpenDoorTime = DateTime.Now; openDoorLogDetails.isOpenSuccessful = false; openDoorLogDetails.LogMessage = logMessage; await _openDoorLogRepository.InsertAsync(openDoorLogDetails); await CurrentUnitOfWork.SaveChangesAsync(); throw new UserFriendlyException(logMessage); }
Swagger返回结果
{"result": null,"targetUrl": null,"success": false,"error": {"code": 0,"message": "The door cannot be unlocked. Your booking is not yet in effect. It is valid from 09:00:00 to 10:00:00. Please try again later.","details": null,"validationErrors": null},"unAuthorizedRequest": false,"__abp": true}
原因分析
这是因为ABP框架中,当前请求的工作单元(UnitOfWork)会在请求结束时自动回滚——即使手动调用了SaveChangesAsync(),后续抛出的异常会触发整个工作单元的事务回滚,导致之前写入的日志被撤销。
解决办法
需要为日志插入操作创建独立的工作单元,脱离当前请求的事务上下文,确保日志写入不受后续异常影响,具体有两种实现方式:
方式1:使用IUnitOfWorkManager开启独立工作单元
if (isTodayOpen) //today open but time not valid { var logMessage = string.Format("The door cannot be unlocked. Your booking is not yet in effect. It is valid from {0} to {1}. Please try again later.", application.Rules.TimeFrom.ToString(), application.Rules.TimeTo.ToString()); // 开启独立于当前事务的工作单元 using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) { var openDoorLogDetails = new OpenDoorLog(); openDoorLogDetails.ApplicantId = application.User.Id; openDoorLogDetails.OpenDoorTime = DateTime.Now; openDoorLogDetails.isOpenSuccessful = false; openDoorLogDetails.LogMessage = logMessage; await _openDoorLogRepository.InsertAsync(openDoorLogDetails); await CurrentUnitOfWork.SaveChangesAsync(); // 手动提交独立工作单元,确保日志写入 await uow.CompleteAsync(); } throw new UserFriendlyException(logMessage); }
方式2:在日志仓储方法上标记非事务特性
如果使用自定义OpenDoorLogRepository,可以在插入方法上添加[UnitOfWork(IsTransactional = false)]特性,让该操作不参与当前事务:
public class OpenDoorLogRepository : EfCoreRepository<MyDbContext, OpenDoorLog>, IOpenDoorLogRepository { public OpenDoorLogRepository(IDbContextProvider<MyDbContext> dbContextProvider) : base(dbContextProvider) { } [UnitOfWork(IsTransactional = false)] public override async Task InsertAsync(OpenDoorLog entity, bool autoSave = false, CancellationToken cancellationToken = default) { await base.InsertAsync(entity, autoSave, cancellationToken); } }
这种方式下,调用InsertAsync时设置autoSave=true(或手动调用SaveChangesAsync),日志会直接写入数据库,不受后续事务回滚影响。
内容的提问来源于stack exchange,提问作者Chua Hong Yong
相关产品推荐
相关产品推荐

