| | | 1 | | using Elsa.Common.Entities; |
| | | 2 | | using Elsa.Common.Multitenancy; |
| | | 3 | | using Elsa.Extensions; |
| | | 4 | | using Elsa.Tenants.Options; |
| | | 5 | | using Microsoft.EntityFrameworkCore; |
| | | 6 | | using Microsoft.EntityFrameworkCore.ChangeTracking; |
| | | 7 | | using Microsoft.Extensions.DependencyInjection; |
| | | 8 | | using Microsoft.Extensions.Options; |
| | | 9 | | |
| | | 10 | | namespace 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> |
| | | 15 | | public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema |
| | | 16 | | { |
| | 1 | 17 | | private static readonly ISet<EntityState> ModifiedEntityStates = new HashSet<EntityState> |
| | 1 | 18 | | { |
| | 1 | 19 | | EntityState.Added, |
| | 1 | 20 | | EntityState.Modified, |
| | 1 | 21 | | }; |
| | | 22 | | |
| | 936 | 23 | | protected IServiceProvider ServiceProvider { get; } |
| | | 24 | | private readonly ElsaDbContextOptions? _elsaDbContextOptions; |
| | 143474 | 25 | | public string? TenantId { get; set; } |
| | | 26 | | |
| | 18760 | 27 | | public bool IsTenantFilteringEnabled { get; } |
| | | 28 | | |
| | | 29 | | /// <summary> |
| | | 30 | | /// The default schema used by Elsa. |
| | | 31 | | /// </summary> |
| | 25181 | 32 | | public static string ElsaSchema { get; set; } = "Elsa"; |
| | | 33 | | |
| | | 34 | | /// <inheritdoc/> |
| | 273 | 35 | | public string Schema { get; } |
| | | 36 | | |
| | | 37 | | /// <summary> |
| | | 38 | | /// The table used to store the migrations history. |
| | | 39 | | /// </summary> |
| | 2386 | 40 | | public static string MigrationsHistoryTable { get; set; } = "__EFMigrationsHistory"; |
| | | 41 | | |
| | | 42 | | /// <summary> |
| | | 43 | | /// Initializes a new instance of the <see cref="ElsaDbContextBase"/> class. |
| | | 44 | | /// </summary> |
| | 22795 | 45 | | protected ElsaDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options) |
| | | 46 | | { |
| | 22795 | 47 | | ServiceProvider = serviceProvider; |
| | 22795 | 48 | | _elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options; |
| | | 49 | | |
| | | 50 | | // ReSharper disable once VirtualMemberCallInConstructor |
| | 22795 | 51 | | Schema = !string.IsNullOrWhiteSpace(_elsaDbContextOptions?.SchemaName) ? _elsaDbContextOptions.SchemaName : Elsa |
| | | 52 | | |
| | 22795 | 53 | | IsTenantFilteringEnabled = serviceProvider.GetService<IOptions<TenantsOptions>>()?.Value.IsEnabled ?? false; |
| | | 54 | | |
| | 22795 | 55 | | var tenantAccessor = serviceProvider.GetService<ITenantAccessor>(); |
| | 22795 | 56 | | var tenantId = (tenantAccessor?.TenantId).NormalizeTenantId(); |
| | 22795 | 57 | | TenantId ??= tenantId; |
| | 22795 | 58 | | } |
| | | 59 | | |
| | | 60 | | /// <inheritdoc/> |
| | | 61 | | public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) |
| | | 62 | | { |
| | 930 | 63 | | await OnBeforeSavingAsync(cancellationToken); |
| | 930 | 64 | | return await base.SaveChangesAsync(cancellationToken); |
| | 927 | 65 | | } |
| | | 66 | | |
| | | 67 | | /// <inheritdoc /> |
| | | 68 | | protected override void OnModelCreating(ModelBuilder modelBuilder) |
| | | 69 | | { |
| | 6 | 70 | | if (!string.IsNullOrWhiteSpace(Schema)) |
| | 6 | 71 | | modelBuilder.HasDefaultSchema(Schema); |
| | | 72 | | |
| | 6 | 73 | | var additionalConfigurations = _elsaDbContextOptions?.GetModelConfigurations(this); |
| | | 74 | | |
| | 6 | 75 | | additionalConfigurations?.Invoke(modelBuilder); |
| | | 76 | | |
| | 6 | 77 | | using var scope = ServiceProvider.CreateScope(); |
| | 6 | 78 | | var entityTypeHandlers = scope.ServiceProvider.GetServices<IEntityModelCreatingHandler>().ToList(); |
| | | 79 | | |
| | 80 | 80 | | foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToList()) |
| | | 81 | | { |
| | 502 | 82 | | foreach (var handler in entityTypeHandlers) |
| | 217 | 83 | | handler.Handle(this, modelBuilder, entityType); |
| | | 84 | | } |
| | 6 | 85 | | } |
| | | 86 | | |
| | | 87 | | private async Task OnBeforeSavingAsync(CancellationToken cancellationToken) |
| | | 88 | | { |
| | 930 | 89 | | using var scope = ServiceProvider.CreateScope(); |
| | 930 | 90 | | var handlers = scope.ServiceProvider.GetServices<IEntitySavingHandler>().ToList(); |
| | 13486 | 91 | | foreach (var entry in ChangeTracker.Entries().Where(IsModifiedEntity)) |
| | | 92 | | { |
| | 81382 | 93 | | foreach (var handler in handlers) |
| | 34878 | 94 | | await handler.HandleAsync(this, entry, cancellationToken); |
| | 5813 | 95 | | } |
| | 930 | 96 | | } |
| | | 97 | | |
| | | 98 | | /// <summary> |
| | | 99 | | /// Determine if an entity was modified. |
| | | 100 | | /// </summary> |
| | | 101 | | private bool IsModifiedEntity(EntityEntry entityEntry) |
| | | 102 | | { |
| | 5822 | 103 | | return ModifiedEntityStates.Contains(entityEntry.State) && entityEntry.Entity is Entity; |
| | | 104 | | } |
| | | 105 | | } |