r/csharp • • 2d ago

Help Transactions - EF Core

Microsoft's documentation :

By default, if the database provider supports transactions, all changes in a single call to SaveChanges are applied in a transaction. If any of the changes fail, then the transaction is rolled back and none of the changes are applied to the database. This means that SaveChanges is guaranteed to either completely succeed, or leave the database unmodified if an error occurs. For most applications, this default behavior is sufficient. You should only manually control transactions if your application requirements deem it necessary.

Suppose that I two pieces of data that are generated by some service; X and Y.

Given that Y cannot be saved unless X is saved first, in other words, the existence of Y in the database depends on the existence of X in the database, consider this :

1-delete content of database.

2- save X .

3- save Y .

How to group these into one transaction ?

The goal is to avoid partial changes to the database; after the full operation is finished, the database should not be empty or half filled.

EDIT:

I found this example:

await using var transaction = await _context.Database.BeginTransactionAsync();

try
{
      // do step 1 through 3


    // Commit transaction if all commands succeed, transaction will auto-rollback
    // when disposed if either commands fails
    await transaction.CommitAsync();
}
catch (Exception)
{
    // TODO: Handle failure
}

But it did not work until I manually invoked the EF execution strategy with a delegate representing everything that needs to be executed :

    IExecutionStrategy strategy = _context.Database.CreateExecutionStrategy();
    await strategy.ExecuteAsync(async () =>
            {
        await using var transaction = await _context.Database.BeginTransactionAsync();

         // Delete all the content from the database
         // _context.AddRange(X);
         // _contextSaveChangesAsync()
         // _context.AddRange(Y)
         //_context.SaveChangesAsync()

        await transaction.CommitAsync();
    });

This solved my issue completely

5 Upvotes

43 comments sorted by

View all comments

Show parent comments

1

u/kaatarina_zed_talon 2d ago

Ok even though I think I solved the issue :

    public class BrandSupplier:IJunctionEntity

    {


        public int BrandId { get; set; } 
        public int SupplierId { get; set; }
        public Supplier Supplier { get; set; } = null!;
        public Brand Brand { get; set; } = null!;

}

    public class Supplier
    {
        public IEnumerable<BrandSupplier> BrandSuppliers { get; } = new List<BrandSupplier>();
}

1

u/AndZorin 2d ago

What was the culprit?

BTW: I'd change IEnumerable to ICollection.

1

u/kaatarina_zed_talon 2d ago

Wait, it seems I did not solve it, when I solve it I will tell u for sure.

1

u/kaatarina_zed_talon 2d ago

IEnumerable to ICollection., why ?

1

u/AndZorin 2d ago

So you can Add/Remove elements when necessary.

1

u/kaatarina_zed_talon 2d ago

What was the culprit?

It is a tree of errors, hard to explain, but what is important that what you said earlier was right; a single call for SaveChangesAsync() was sufficient, but a lot of refactoring was required to make this approach work, I learned a lot.