jeudi 28 avril 2022

Repository and UnitOfWork patterns using transactions

First of all I work with C# (ASP.Net Core 3.1 in this case) but the question is more generic about design pattern.

So I ran into an issue using the repositories pattern. I needed to have transactions for my repositories and I found the UnitOfWork pattern that I was not aware of. I implemented it in the way that while EF Core SaveChanges is like a transaction itself when there is only one call to SaveChanges (which should be the case with the UnitOfWork if possible) then I don't need to have a "proper transaction". All was working fine than I need to do something basic : I create an entity in the DB and then create an email in which I need the ID of this entity.

The process is the following :

  • Create the entity
  • Create the email content
  • Save the entity
  • Send the email

The issue is that the ID of the entity is generated on the insert in the DB and if I call SaveChanges after creating the email content the ID is not set. But if the creation of the email content fail I don't want the insert to be done in the DB. So I was like "hum, I really need a transaction after all". So I did the following in my UnitOfWork:

public async Task OpenTransaction()
{
    if (_dbTransaction == null)
    {
        _dbTransaction = await _dbContext.Database.BeginTransactionAsync().ConfigureAwait(false);
    }
}

/// <inheritdoc />
public async Task CommitTransaction()
{
    if (_dbTransaction != null)
    {
        try
        {
            await _dbTransaction.CommitAsync().ConfigureAwait(false);
        }
        catch (Exception)
        {
            await _dbTransaction.RollbackAsync().ConfigureAwait(false);
            _dbTransaction.Dispose();
            _dbTransaction = null;
            throw;
        }
    }
    
    _dbTransaction.Dispose();
    _dbTransaction = null;
}

And the my process become the following :

  • Open a transaction
  • Create the entity
  • Create the email content
  • Commit the transaction

And it's working but I'm afraid that I introduced some kind of "anti-pattern".

Does someone had a similar issue? What do you think of the solution I choose? Is there a better way or another pattern to solve this ?

Thanks a lot

Aucun commentaire:

Enregistrer un commentaire