< Summary

Information
Class: ArturRios.Data.MongoDb.Transactions.MongoUnitOfWork
Assembly: ArturRios.Data.MongoDb
File(s): /home/runner/work/dotnet-data/dotnet-data/src/ArturRios.Data.MongoDb/Transactions/MongoUnitOfWork.cs
Line coverage
36%
Covered lines: 26
Uncovered lines: 45
Coverable lines: 71
Total lines: 164
Line coverage: 36.6%
Branch coverage
81%
Covered branches: 13
Total branches: 16
Branch coverage: 81.2%
Method coverage
57%
Covered methods: 4
Fully covered methods: 1
Total methods: 7
Method coverage: 57.1%
Full method coverage: 14.2%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ExecuteInTransactionAsync()87.5%8892.85%
ExecuteInTransactionAsync()75%11864.28%
ExecuteInTransaction(...)100%210%
ExecuteInTransaction(...)100%210%
AbortQuietlyAsync()100%1160%
AbortQuietly(...)100%210%

File(s)

/home/runner/work/dotnet-data/dotnet-data/src/ArturRios.Data.MongoDb/Transactions/MongoUnitOfWork.cs

#LineLine coverage
 1using ArturRios.Data.MongoDb.Repositories;
 2using ArturRios.Output;
 3using MongoDB.Driver;
 4
 5namespace ArturRios.Data.MongoDb.Transactions;
 6
 7/// <summary>
 8///     MongoDB implementation of the unit of work. Opens a client session, sets it as the context's
 9///     ambient session so repository operations enlist, and commits/aborts the transaction.
 10/// </summary>
 11/// <param name="client">The Mongo client.</param>
 12/// <param name="context">The Mongo context whose ambient session is managed.</param>
 1613public class MongoUnitOfWork(IMongoClient client, MongoContext context) : IMongoUnitOfWork, IAsyncMongoUnitOfWork
 14{
 15    /// <inheritdoc />
 16    public async Task<ProcessOutput> ExecuteInTransactionAsync(Func<Task> work, CancellationToken ct = default)
 17    {
 1018        using var session = await client.StartSessionAsync(cancellationToken: ct).ConfigureAwait(false);
 1019        var previousSession = context.Session;
 1020        context.Session = session;
 1021        session.StartTransaction();
 22        try
 23        {
 1024            await work().ConfigureAwait(false);
 625            await session.CommitTransactionAsync(ct).ConfigureAwait(false);
 626            return ProcessOutput.New;
 27        }
 428        catch (Exception ex)
 29        {
 430            await AbortQuietlyAsync(session).ConfigureAwait(false);
 31
 432            if (ex is OperationCanceledException)
 33            {
 034                throw;
 35            }
 36
 437            return ProcessOutput.New.WithError(MongoErrors.Describe(ex));
 38        }
 39        finally
 40        {
 1041            context.Session = previousSession;
 42        }
 1043    }
 44
 45    /// <inheritdoc />
 46    public async Task<DataOutput<TResult>> ExecuteInTransactionAsync<TResult>(Func<Task<TResult>> work,
 47        CancellationToken ct = default)
 48    {
 249        using var session = await client.StartSessionAsync(cancellationToken: ct).ConfigureAwait(false);
 250        var previousSession = context.Session;
 251        context.Session = session;
 252        session.StartTransaction();
 53        try
 54        {
 255            var result = await work().ConfigureAwait(false);
 256            await session.CommitTransactionAsync(ct).ConfigureAwait(false);
 257            return DataOutput<TResult>.New.WithData(result);
 58        }
 059        catch (Exception ex)
 60        {
 061            await AbortQuietlyAsync(session).ConfigureAwait(false);
 62
 063            if (ex is OperationCanceledException)
 64            {
 065                throw;
 66            }
 67
 068            return DataOutput<TResult>.New.WithError(MongoErrors.Describe(ex));
 69        }
 70        finally
 71        {
 272            context.Session = previousSession;
 73        }
 274    }
 75
 76    /// <inheritdoc />
 77    public ProcessOutput ExecuteInTransaction(Action work)
 78    {
 079        using var session = client.StartSession();
 080        var previousSession = context.Session;
 081        context.Session = session;
 082        session.StartTransaction();
 83        try
 84        {
 085            work();
 086            session.CommitTransaction();
 087            return ProcessOutput.New;
 88        }
 089        catch (OperationCanceledException)
 90        {
 091            AbortQuietly(session);
 92
 093            throw;
 94        }
 095        catch (Exception ex)
 96        {
 097            AbortQuietly(session);
 98
 099            return ProcessOutput.New.WithError(MongoErrors.Describe(ex));
 100        }
 101        finally
 102        {
 0103            context.Session = previousSession;
 0104        }
 0105    }
 106
 107    /// <inheritdoc />
 108    public DataOutput<TResult> ExecuteInTransaction<TResult>(Func<TResult> work)
 109    {
 0110        using var session = client.StartSession();
 0111        var previousSession = context.Session;
 0112        context.Session = session;
 0113        session.StartTransaction();
 114        try
 115        {
 0116            var result = work();
 0117            session.CommitTransaction();
 0118            return DataOutput<TResult>.New.WithData(result);
 119        }
 0120        catch (OperationCanceledException)
 121        {
 0122            AbortQuietly(session);
 123
 0124            throw;
 125        }
 0126        catch (Exception ex)
 127        {
 0128            AbortQuietly(session);
 129
 0130            return DataOutput<TResult>.New.WithError(MongoErrors.Describe(ex));
 131        }
 132        finally
 133        {
 0134            context.Session = previousSession;
 0135        }
 0136    }
 137
 138    // Abort must never mask the failure that triggered it: the server aborts the transaction itself
 139    // on a write conflict, so an explicit abort can fail on a transaction that is already gone. It
 140    // also runs untied to the caller's token, which may already be canceled.
 141    private static async Task AbortQuietlyAsync(IClientSessionHandle session)
 142    {
 143        try
 144        {
 4145            await session.AbortTransactionAsync(CancellationToken.None).ConfigureAwait(false);
 4146        }
 0147        catch
 148        {
 149            // Already aborted, or the session is gone. Disposing the session completes the cleanup.
 0150        }
 4151    }
 152
 153    private static void AbortQuietly(IClientSessionHandle session)
 154    {
 155        try
 156        {
 0157            session.AbortTransaction();
 0158        }
 0159        catch
 160        {
 161            // Already aborted, or the session is gone. Disposing the session completes the cleanup.
 0162        }
 0163    }
 164}