< Summary

Information
Class: ArturRios.Data.Relational.Core.Configuration.BaseDbContext
Assembly: ArturRios.Data.Relational.Core
File(s): /home/ci/agent/workspace/dotnet-data-docs_main/app/src/ArturRios.Data.Relational.Core/Configuration/BaseDbContext.cs
Line coverage
100%
Covered lines: 22
Uncovered lines: 0
Coverable lines: 22
Total lines: 73
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage
100%
Covered methods: 5
Fully covered methods: 5
Total methods: 5
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
SaveChanges(...)100%11100%
SaveChangesAsync()100%11100%
BumpConcurrencyStamps()100%22100%
RestoreConcurrencyStamps(...)100%22100%

File(s)

/home/ci/agent/workspace/dotnet-data-docs_main/app/src/ArturRios.Data.Relational.Core/Configuration/BaseDbContext.cs

#LineLine coverage
 1using ArturRios.Data.Relational.Core.Entities;
 2using Microsoft.EntityFrameworkCore;
 3
 4namespace ArturRios.Data.Relational.Core.Configuration;
 5
 6/// <summary>
 7///     Base <see cref="DbContext" /> that applies shared conventions and refreshes the
 8///     optimistic-concurrency stamp of modified <see cref="IVersionedEntity" /> instances on save.
 9/// </summary>
 10/// <param name="options">The context options supplied by the configured provider.</param>
 15411public abstract class BaseDbContext(DbContextOptions options) : DbContext(options)
 12{
 13    // The parameterless SaveChanges / SaveChangesAsync overloads delegate to these two, so overriding
 14    // them covers every save entry point.
 15
 16    /// <inheritdoc />
 17    public override int SaveChanges(bool acceptAllChangesOnSuccess)
 18    {
 13019        var bumped = BumpConcurrencyStamps();
 20
 21        try
 22        {
 13023            return base.SaveChanges(acceptAllChangesOnSuccess);
 24        }
 2025        catch
 26        {
 2027            RestoreConcurrencyStamps(bumped);
 2028            throw;
 29        }
 11030    }
 31
 32    /// <inheritdoc />
 33    public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess,
 34        CancellationToken cancellationToken = default)
 35    {
 3636        var bumped = BumpConcurrencyStamps();
 37
 38        try
 39        {
 3640            return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false);
 41        }
 242        catch
 43        {
 244            RestoreConcurrencyStamps(bumped);
 245            throw;
 46        }
 3447    }
 48
 49    private List<(IVersionedEntity Entity, Guid Stamp)> BumpConcurrencyStamps()
 50    {
 16651        var bumped = new List<(IVersionedEntity Entity, Guid Stamp)>();
 52
 38453        foreach (var entry in ChangeTracker.Entries<IVersionedEntity>()
 21854                     .Where(e => e.State == EntityState.Modified))
 55        {
 2656            bumped.Add((entry.Entity, entry.Entity.ConcurrencyStamp));
 2657            entry.Entity.ConcurrencyStamp = Guid.NewGuid();
 58        }
 59
 16660        return bumped;
 61    }
 62
 63    // The save never landed, so the in-memory bump must not survive: keeping it would leave the
 64    // entity carrying a stamp the database never stored, and every retry would then fail as a
 65    // concurrency conflict.
 66    private static void RestoreConcurrencyStamps(List<(IVersionedEntity Entity, Guid Stamp)> bumped)
 67    {
 6468        foreach (var (entity, stamp) in bumped)
 69        {
 1070            entity.ConcurrencyStamp = stamp;
 71        }
 2272    }
 73}