< Summary

Line coverage
100%
Covered lines: 57
Uncovered lines: 0
Coverable lines: 57
Total lines: 180
Line coverage: 100%
Branch coverage
100%
Covered branches: 18
Total branches: 18
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Infrastructure/Data/ApplicationDbContext.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.EntityFrameworkCore.Metadata;
 3using Microsoft.Extensions.Logging;
 4using ProjectTemplate.Infrastructure.Data.Entities;
 5
 6namespace ProjectTemplate.Infrastructure.Data;
 7
 8/// <summary>
 9/// Represents the EF Core database context for the ProjectTemplate application.
 10/// </summary>
 11public sealed partial class ApplicationDbContext(
 12    DbContextOptions<ApplicationDbContext> options,
 13    ILogger<ApplicationDbContext> logger,
 14    ApplicationSaveChangesInterceptor saveChangesInterceptor
 15)
 11716    : DbContext(options)
 17{
 11818    private readonly ILogger<ApplicationDbContext> _logger = logger;
 11819    private readonly ApplicationSaveChangesInterceptor _saveChangesInterceptor =
 11820        saveChangesInterceptor ?? throw new ArgumentNullException(nameof(saveChangesInterceptor));
 21
 22    /// <summary>
 23    /// Gets the audit records for the application.
 24    /// </summary>
 12725    public DbSet<AuditRecord> AuditRecords => Set<AuditRecord>();
 26
 27    /// <summary>
 28    /// Gets the durable, minimized audit-completion outbox entries.
 29    /// </summary>
 30    public DbSet<ApplicationAuditCompletionOutboxEntry> ApplicationAuditCompletionOutboxEntries =>
 7631        Set<ApplicationAuditCompletionOutboxEntry>();
 32
 33    /// <summary>
 34    /// Gets the external login account links for the application.
 35    /// </summary>
 8936    public DbSet<ExternalLoginAccount> ExternalLoginAccounts => Set<ExternalLoginAccount>();
 37
 38    [LoggerMessage(
 39        EventId = 19001,
 40        Level = LogLevel.Warning,
 41        Message = "Optimistic concurrency conflict detected while saving {EntryCount} tracked entity entries.")]
 42    private static partial void LogOptimisticConcurrencyConflict(
 43        ILogger logger,
 44        int entryCount,
 45        Exception exception);
 46
 47    /// <inheritdoc />
 48    protected override void OnModelCreating(ModelBuilder modelBuilder)
 49    {
 550        ArgumentNullException.ThrowIfNull(modelBuilder);
 51
 552        _ = modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
 553        ConfigureDataEntityDefaults(modelBuilder);
 554        ConfigureTimestampDefaults(modelBuilder);
 55
 556        base.OnModelCreating(modelBuilder);
 557    }
 58
 59    /// <inheritdoc />
 60    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 61    {
 11662        ArgumentNullException.ThrowIfNull(optionsBuilder);
 63
 11664        _ = optionsBuilder.AddInterceptors(_saveChangesInterceptor);
 65
 11666        base.OnConfiguring(optionsBuilder);
 11667    }
 68
 69    public bool HasUnsavedChanges()
 70    {
 1371        return ChangeTracker.HasChanges();
 72    }
 73
 74    public override int SaveChanges()
 75    {
 1176        return SaveChanges(acceptAllChangesOnSuccess: true);
 77    }
 78
 79    public override int SaveChanges(bool acceptAllChangesOnSuccess = true)
 80    {
 1181        return SaveChangesWithConcurrencyHandling(
 1182            () => base.SaveChanges(acceptAllChangesOnSuccess));
 83    }
 84
 85    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
 86    {
 9387        return SaveChangesAsync(
 9388            acceptAllChangesOnSuccess: true,
 9389            cancellationToken);
 90    }
 91
 92    public override Task<int> SaveChangesAsync(
 93        bool acceptAllChangesOnSuccess,
 94        CancellationToken cancellationToken = default)
 95    {
 9396        return SaveChangesWithConcurrencyHandlingAsync(
 9397            () => base.SaveChangesAsync(
 9398                acceptAllChangesOnSuccess,
 9399                cancellationToken));
 100    }
 101
 102    private static bool IsUtcTimestampProperty(string propertyName)
 103    {
 65104        return propertyName.EndsWith("Utc", StringComparison.Ordinal);
 105    }
 106
 107    private static void ConfigureDataEntityDefaults(ModelBuilder modelBuilder)
 108    {
 60109        foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes())
 110        {
 25111            if (!typeof(DataEntity).IsAssignableFrom(entityType.ClrType))
 112            {
 113                continue;
 114            }
 115
 25116            _ = modelBuilder.Entity(entityType.ClrType)
 25117                .Property<string>(nameof(DataEntity.ConcurrencyStamp))
 25118                .HasMaxLength(64)
 25119                .IsRequired()
 25120                .IsConcurrencyToken();
 121        }
 5122    }
 123
 124    private static void ConfigureTimestampDefaults(ModelBuilder modelBuilder)
 125    {
 60126        foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes())
 127        {
 850128            foreach (IMutableProperty property in entityType.GetProperties())
 129            {
 400130                Type propertyType = Nullable.GetUnderlyingType(property.ClrType)
 400131                    ?? property.ClrType;
 132
 400133                if ((propertyType == typeof(DateTime) || propertyType == typeof(DateTimeOffset)) &&
 400134                    IsUtcTimestampProperty(property.Name))
 135                {
 65136                    property.SetPrecision(PersistenceTimestamp.Precision);
 137                }
 138            }
 139        }
 5140    }
 141
 142    private int SaveChangesWithConcurrencyHandling(Func<int> saveChanges)
 143    {
 144        try
 145        {
 11146            return saveChanges();
 147        }
 1148        catch (DbUpdateConcurrencyException exception)
 149        {
 1150            LogOptimisticConcurrencyConflict(_logger, exception.Entries.Count, exception);
 1151            throw;
 152        }
 10153    }
 154
 155    private async Task<int> SaveChangesWithConcurrencyHandlingAsync(Func<Task<int>> saveChanges)
 156    {
 157        try
 158        {
 93159            return await saveChanges().ConfigureAwait(false);
 160        }
 1161        catch (DbUpdateConcurrencyException exception)
 162        {
 1163            LogOptimisticConcurrencyConflict(_logger, exception.Entries.Count, exception);
 1164            throw;
 165        }
 90166    }
 167}

/home/runner/work/NetCoreApplicationTemplate/NetCoreApplicationTemplate/src/ProjectTemplate.Infrastructure/Data/ApplicationDbContext.Reconciliation.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using ProjectTemplate.Infrastructure.Data.Entities;
 3
 4namespace ProjectTemplate.Infrastructure.Data;
 5
 6public sealed partial class ApplicationDbContext
 7{
 8    public DbSet<ApplicationAuditReconciliationFinding> ApplicationAuditReconciliationFindings =>
 729        Set<ApplicationAuditReconciliationFinding>();
 10
 11    public DbSet<ApplicationAuditReconciliationRemediation> ApplicationAuditReconciliationRemediations =>
 512        Set<ApplicationAuditReconciliationRemediation>();
 13}