DEV Community

Afshar
Afshar

Posted on

1 1

Committing the unit of work

Most of applications I have worked on as a developer involve a sort of unit of work based on database operations. Traditionally, a unit of work starts when a web request begins, and it ends when the request ends. Normally, database transaction is committed at the end of the web request. This is the point that sometimes needs tuning.

In ASP.NET WebForms there were a place called Global.asax. As far as I remember, some sort of automation could have happened there. For instance, calling commit was put here. But, if I remember correctly, if something bad happened in the database, the client would not be informed at all. For example, when an integrity rule raised an error in database due to a delete action triggered by the user, no one in client side would informed about it. The request would have return 200 family while the transaction was rolled back in the back-end. This was causing lot of problems, because none of changes by the user were saved into the database. I have memories of bugs reported by customers just as a result of this behavior.

I was thinking that a perfect automation can not be reached in ASP.NET almost. However, a SO question encouraged me to try it again. Firstly, I created and ActionFilter in ASP.NET Core 2.x as this:

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using TestFilter.Data;

namespace TestFilter
{
    public class TestActionFilter : IActionFilter
    {
        readonly ApplicationDbContext _applicationDbContext;
        public TestActionFilter(IServiceProvider serviceProvider)
        {
            _applicationDbContext = serviceProvider.GetRequiredService<ApplicationDbContext>();
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception == null)
            {
                try
                {
                    _applicationDbContext.SaveChanges();
                }
                catch (Exception exception)
                {
                    context.Result = new BadRequestObjectResult($"Error: {exception.Message}");
                    return;
                }
            }
            else
            {
                throw new Exception("Captured in Filter", context.Exception);
            }
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

In the catch section, I can receive possible database errors, hence reflecting it to the user. This way, I am not forced to add SaveChanges to every single actions. Not only this solution works with ASP.NET Core MVC, but also it works with ASP.NET Core API too. Thanks to ASP.NET Core because either of MVC and API utilize same base controllers.

To having my curiosity satisfied, I removed the try-catch fences from around SaveChanges() and tied to cause a database error. To my surprise, the error come up right in user side rather than being missed. Seems that something has changed during years, and the database error is not going to be missed again. I used SQLite database in the experience.

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay