< Summary

Information
Class: Elsa.Persistence.EFCore.ElsaDbContextBase
Assembly: Elsa.Persistence.EFCore.Common
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore.Common/ElsaDbContextBase.cs
Line coverage
100%
Covered lines: 41
Uncovered lines: 0
Coverable lines: 41
Total lines: 105
Line coverage: 100%
Branch coverage
82%
Covered branches: 23
Total branches: 28
Branch coverage: 82.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
get_ServiceProvider()100%11100%
get_TenantId()100%11100%
get_IsTenantFilteringEnabled()100%11100%
get_ElsaSchema()100%11100%
get_Schema()100%11100%
get_MigrationsHistoryTable()100%11100%
.ctor(...)75%1212100%
SaveChangesAsync()100%11100%
OnModelCreating(...)80%1010100%
OnBeforeSavingAsync()100%44100%
IsModifiedEntity(...)100%22100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore.Common/ElsaDbContextBase.cs

#LineLine coverage
 1using Elsa.Common.Entities;
 2using Elsa.Common.Multitenancy;
 3using Elsa.Extensions;
 4using Elsa.Tenants.Options;
 5using Microsoft.EntityFrameworkCore;
 6using Microsoft.EntityFrameworkCore.ChangeTracking;
 7using Microsoft.Extensions.DependencyInjection;
 8using Microsoft.Extensions.Options;
 9
 10namespace Elsa.Persistence.EFCore;
 11
 12/// <summary>
 13/// An optional base class to implement with some opinions on certain converters to install for certain DB providers.
 14/// </summary>
 15public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema
 16{
 117    private static readonly ISet<EntityState> ModifiedEntityStates = new HashSet<EntityState>
 118    {
 119        EntityState.Added,
 120        EntityState.Modified,
 121    };
 22
 93623    protected IServiceProvider ServiceProvider { get; }
 24    private readonly ElsaDbContextOptions? _elsaDbContextOptions;
 14347425    public string? TenantId { get; set; }
 26
 1876027    public bool IsTenantFilteringEnabled { get; }
 28
 29    /// <summary>
 30    /// The default schema used by Elsa.
 31    /// </summary>
 2518132    public static string ElsaSchema { get; set; } = "Elsa";
 33
 34    /// <inheritdoc/>
 27335    public string Schema { get; }
 36
 37    /// <summary>
 38    /// The table used to store the migrations history.
 39    /// </summary>
 238640    public static string MigrationsHistoryTable { get; set; } = "__EFMigrationsHistory";
 41
 42    /// <summary>
 43    /// Initializes a new instance of the <see cref="ElsaDbContextBase"/> class.
 44    /// </summary>
 2279545    protected ElsaDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options)
 46    {
 2279547        ServiceProvider = serviceProvider;
 2279548        _elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options;
 49
 50        // ReSharper disable once VirtualMemberCallInConstructor
 2279551        Schema = !string.IsNullOrWhiteSpace(_elsaDbContextOptions?.SchemaName) ? _elsaDbContextOptions.SchemaName : Elsa
 52
 2279553        IsTenantFilteringEnabled = serviceProvider.GetService<IOptions<TenantsOptions>>()?.Value.IsEnabled ?? false;
 54
 2279555        var tenantAccessor = serviceProvider.GetService<ITenantAccessor>();
 2279556        var tenantId = (tenantAccessor?.TenantId).NormalizeTenantId();
 2279557        TenantId ??= tenantId;
 2279558    }
 59
 60    /// <inheritdoc/>
 61    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
 62    {
 93063        await OnBeforeSavingAsync(cancellationToken);
 93064        return await base.SaveChangesAsync(cancellationToken);
 92765    }
 66
 67    /// <inheritdoc />
 68    protected override void OnModelCreating(ModelBuilder modelBuilder)
 69    {
 670        if (!string.IsNullOrWhiteSpace(Schema))
 671            modelBuilder.HasDefaultSchema(Schema);
 72
 673        var additionalConfigurations = _elsaDbContextOptions?.GetModelConfigurations(this);
 74
 675        additionalConfigurations?.Invoke(modelBuilder);
 76
 677        using var scope = ServiceProvider.CreateScope();
 678        var entityTypeHandlers = scope.ServiceProvider.GetServices<IEntityModelCreatingHandler>().ToList();
 79
 8080        foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToList())
 81        {
 50282            foreach (var handler in entityTypeHandlers)
 21783                handler.Handle(this, modelBuilder, entityType);
 84        }
 685    }
 86
 87    private async Task OnBeforeSavingAsync(CancellationToken cancellationToken)
 88    {
 93089        using var scope = ServiceProvider.CreateScope();
 93090        var handlers = scope.ServiceProvider.GetServices<IEntitySavingHandler>().ToList();
 1348691        foreach (var entry in ChangeTracker.Entries().Where(IsModifiedEntity))
 92        {
 8138293            foreach (var handler in handlers)
 3487894                await handler.HandleAsync(this, entry, cancellationToken);
 581395        }
 93096    }
 97
 98    /// <summary>
 99    /// Determine if an entity was modified.
 100    /// </summary>
 101    private bool IsModifiedEntity(EntityEntry entityEntry)
 102    {
 5822103        return ModifiedEntityStates.Contains(entityEntry.State) && entityEntry.Entity is Entity;
 104    }
 105}