Committing the unit of work
Tags: aspnet, entityframework, architecture
Published: 2019-09-17 10:30:28

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:

```csharp
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)
        {
        }
    }
}
```


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.