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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 13:21:31