# Implementing Transaction-Like Behavior in C#

Sometimes, it would be great to have a mechanism to create database-like transactions in code, where you have atomicity guarantees, meaning that either all operations happen or none. One example might be that you compile an email notification with To Dos but want to discard the email if there are no To Dos.

I implemented a short sample that shows how it can be done; this should work very similarly in other languages too.

## Basic Usage

To start a transaction, new up an instance of `LightTransaction`.

```csharp
using var tx = new Transactions.LightTransaction();
```

The transaction implements `IDisposable`, so make use of the using pattern.

Now you can add synchronous and asynchronous operations to the transaction.

```csharp
tx.Add(() => Console.WriteLine("Synchronous operation #1 executed."));
tx2.Add(ct =>
            {
                Console.WriteLine("Asynchronous operation #1 executed.");
                return Task.CompletedTask;
            });
```

To commit the transaction, call `CommitAsync`.

```csharp
await tx.CommitAsync(CancellationToken.None);
```

Calling `CommitAsync` means that all added operations (synchronous and asynchronous) will be executed in their registration order. If `CommitAsync` is never called, none of the registered actions get executed.

## Verify

If you would like a way for operations to fail, you can register a `Verify` action. Verify actions will be executed before the main actions. If one throws an exception, no actions will be executed at all.

```csharp
using var tx = new Transactions.LightTransaction();
tx.Add(() => Console.WriteLine("Synchronous operation #1 executed."), () => Console.WriteLine("Synchronous operation #1 verified."));
tx.Add(() => Console.WriteLine("Synchronous operation #2 executed."), () => throw new InvalidOperationException("duh!"));
await tx.CommitAsync(CancellationToken.None);
```

In this case, `Verify #1` is called, and `Verify #2` fails; hence `Action #1` and `Action #2` never get executed. Verify works the same for asynchronous actions.

## Conclusion

This will, of course, only work if you can guarantee that the operations themselves don’t fail. Nevertheless, I think the pattern can be helpful in many scenarios and will make the code more readable than implementing this behavior manually in your business logic.

## Source Code

You can find the source code for this post on [Codeberg](https://codeberg.org/aschommer/LightTransaction).
