Open Closed

Unit of work not performing rollback on exception in appservice #939


0
pontus created

Check the docs before asking a question: https://docs.abp.io/en/commercial/latest/ Check the samples, to see the basic tasks: https://docs.abp.io/en/commercial/latest/samples/index The exact solution to your question may have been answered before, please use the search on the homepage.

  • ABP Framework version: v4.2.1
  • UI type: Angular
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): yes
  • Exception message and stack trace:
  • Steps to reproduce the issue: Created brand new solution with the abp cli. Created a book entity and a db set. Created the migration and and added an app service.

(The BookStoreAppService inherits from AppService).

When i try this from swagger i get the 500 response as expected, however the book is still saved to the database.

Is there something obvoius that I'm missing? Somehow it seems to serious to have been overlooked :)


8 Answer(s)
  • 0
    liangshiwei created
    Support Team

    I can't reproduce your problem.

    Can you provide a project to reproduce? thanks.

  • 0
    pontus created

    https://github.com/PontusLjungdahl/abpbook2

  • 0
    Moyaoxiang created
    Support Team

    Hi pontus, You only need to delete the app.UseUnitOfWork(); statement in BookStoreHttpApiHostModule. the reason: ABP will use AbpUowActionFilter to process work units by default. After adding middleware, it will use AbpUnitOfWorkMiddleware to create work units first. In AbpUowActionFilter, the original Exception was swallowed, and AbpUnitOfWorkMiddleware considered that no exception occurred, so the data was submitted to the database. I think this is a bug.

  • 0
    pontus created

    Thank you.

  • 0
    liangshiwei created
    Support Team

    Hi,

    https://github.com/PontusLjungdahl/abpbook2

    Please remove the repo on Github, because the source code has your linence key, it's not safe for you.

  • 0
    pontus created

    Hi,

    https://github.com/PontusLjungdahl/abpbook2

    Please remove the repo on Github, because the source code has your linence key, it's not safe for you.

    Thank you, it's removed.

  • 0
    maliming created
    Support Team

    https://github.com/abpframework/abp/pull/7804

  • 0
    liangshiwei created
    Support Team

    HI,

    We will fix it by https://github.com/abpframework/abp/pull/7804

    For now , you can try:

    public class MyAbpUowActionFilter : IAsyncActionFilter, ITransientDependency
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (!context.ActionDescriptor.IsControllerAction())
            {
                await next();
                return;
            }
    
            var methodInfo = context.ActionDescriptor.GetMethodInfo();
            var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(methodInfo);
    
            context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext
            {
                IsObjectResult = context.ActionDescriptor.HasObjectResult()
            };
    
            if (unitOfWorkAttr?.IsDisabled == true)
            {
                await next();
                return;
            }
    
            var options = CreateOptions(context, unitOfWorkAttr);
    
            var unitOfWorkManager = context.HttpContext.RequestServices.GetRequiredService<IUnitOfWorkManager>();
    
            //Trying to begin a reserved UOW by AbpUnitOfWorkMiddleware
            if (unitOfWorkManager.TryBeginReserved(UnitOfWork.UnitOfWorkReservationName, options))
            {
                var result = await next();
                if (!Succeed(result))
                {
                    await RollbackAsync(context, unitOfWorkManager);
                }
    
                return;
            }
    
            //Begin a new, independent unit of work
            using (var uow = unitOfWorkManager.Begin(options))
            {
                var result = await next();
                if (Succeed(result))
                {
                    await uow.CompleteAsync(context.HttpContext.RequestAborted);
                }
                else
                {
                    await uow.RollbackAsync(context.HttpContext.RequestAborted);
                }
            }
        }
    
        private AbpUnitOfWorkOptions CreateOptions(ActionExecutingContext context,
            UnitOfWorkAttribute unitOfWorkAttribute)
        {
            var options = new AbpUnitOfWorkOptions();
    
            unitOfWorkAttribute?.SetOptions(options);
    
            if (unitOfWorkAttribute?.IsTransactional == null)
            {
                var abpUnitOfWorkDefaultOptions =
                    context.HttpContext.RequestServices.GetRequiredService<IOptions<AbpUnitOfWorkDefaultOptions>>().Value;
                options.IsTransactional = abpUnitOfWorkDefaultOptions.CalculateIsTransactional(
                    autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method,
                        StringComparison.OrdinalIgnoreCase)
                );
            }
    
            return options;
        }
    
        private async Task RollbackAsync(ActionExecutingContext context, IUnitOfWorkManager unitOfWorkManager)
        {
            var currentUow = unitOfWorkManager.Current;
            if (currentUow != null)
            {
                await currentUow.RollbackAsync(context.HttpContext.RequestAborted);
            }
        }
    
        private static bool Succeed(ActionExecutedContext result)
        {
            return result.Exception == null || result.ExceptionHandled;
        }
    }
    
    Configure<MvcOptions>(options =>
    {
        options.Filters.RemoveAll(x => x.GetType() == typeof(AbpUowActionFilter));
        options.Filters.Add<MyAbpUowActionFilter>();
    });
    

    AbpUowPageFilter also.