| | | 1 | | using Elsa.Common.Entities; |
| | | 2 | | using Elsa.Common.Multitenancy; |
| | | 3 | | using Elsa.Extensions; |
| | | 4 | | using Microsoft.EntityFrameworkCore; |
| | | 5 | | using Microsoft.EntityFrameworkCore.ChangeTracking; |
| | | 6 | | using Microsoft.Extensions.DependencyInjection; |
| | | 7 | | |
| | | 8 | | namespace Elsa.Persistence.EFCore; |
| | | 9 | | |
| | | 10 | | /// <summary> |
| | | 11 | | /// An optional base class to implement with some opinions on certain converters to install for certain DB providers. |
| | | 12 | | /// </summary> |
| | | 13 | | public abstract class ElsaDbContextBase : DbContext, IElsaDbContextSchema |
| | | 14 | | { |
| | 1 | 15 | | private static readonly ISet<EntityState> ModifiedEntityStates = new HashSet<EntityState> |
| | 1 | 16 | | { |
| | 1 | 17 | | EntityState.Added, |
| | 1 | 18 | | EntityState.Modified, |
| | 1 | 19 | | }; |
| | | 20 | | |
| | 771 | 21 | | protected IServiceProvider ServiceProvider { get; } |
| | | 22 | | private readonly ElsaDbContextOptions? _elsaDbContextOptions; |
| | 12663 | 23 | | public string? TenantId { get; set; } |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// The default schema used by Elsa. |
| | | 27 | | /// </summary> |
| | 7653 | 28 | | public static string ElsaSchema { get; set; } = "Elsa"; |
| | | 29 | | |
| | | 30 | | /// <inheritdoc/> |
| | 312 | 31 | | public string Schema { get; } |
| | | 32 | | |
| | | 33 | | /// <summary> |
| | | 34 | | /// The table used to store the migrations history. |
| | | 35 | | /// </summary> |
| | 1654 | 36 | | public static string MigrationsHistoryTable { get; set; } = "__EFMigrationsHistory"; |
| | | 37 | | |
| | | 38 | | /// <summary> |
| | | 39 | | /// Initializes a new instance of the <see cref="ElsaDbContextBase"/> class. |
| | | 40 | | /// </summary> |
| | 5999 | 41 | | protected ElsaDbContextBase(DbContextOptions options, IServiceProvider serviceProvider) : base(options) |
| | | 42 | | { |
| | 5999 | 43 | | ServiceProvider = serviceProvider; |
| | 5999 | 44 | | _elsaDbContextOptions = options.FindExtension<ElsaDbContextOptionsExtension>()?.Options; |
| | | 45 | | |
| | | 46 | | // ReSharper disable once VirtualMemberCallInConstructor |
| | 5999 | 47 | | Schema = !string.IsNullOrWhiteSpace(_elsaDbContextOptions?.SchemaName) ? _elsaDbContextOptions.SchemaName : Elsa |
| | | 48 | | |
| | 5999 | 49 | | var tenantAccessor = serviceProvider.GetService<ITenantAccessor>(); |
| | 5999 | 50 | | var tenantId = tenantAccessor?.Tenant?.Id; |
| | | 51 | | |
| | 5999 | 52 | | if (!string.IsNullOrWhiteSpace(tenantId)) |
| | 0 | 53 | | TenantId = tenantId.NullIfEmpty(); |
| | 5999 | 54 | | } |
| | | 55 | | |
| | | 56 | | /// <inheritdoc/> |
| | | 57 | | public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) |
| | | 58 | | { |
| | 767 | 59 | | await OnBeforeSavingAsync(cancellationToken); |
| | 767 | 60 | | return await base.SaveChangesAsync(cancellationToken); |
| | 767 | 61 | | } |
| | | 62 | | |
| | | 63 | | /// <inheritdoc /> |
| | | 64 | | protected override void OnModelCreating(ModelBuilder modelBuilder) |
| | | 65 | | { |
| | 4 | 66 | | if (!string.IsNullOrWhiteSpace(Schema)) |
| | 4 | 67 | | modelBuilder.HasDefaultSchema(Schema); |
| | | 68 | | |
| | 4 | 69 | | var additionalConfigurations = _elsaDbContextOptions?.GetModelConfigurations(this); |
| | | 70 | | |
| | 4 | 71 | | additionalConfigurations?.Invoke(modelBuilder); |
| | | 72 | | |
| | 4 | 73 | | using var scope = ServiceProvider.CreateScope(); |
| | 4 | 74 | | var entityTypeHandlers = scope.ServiceProvider.GetServices<IEntityModelCreatingHandler>().ToList(); |
| | | 75 | | |
| | 66 | 76 | | foreach (var entityType in modelBuilder.Model.GetEntityTypes().ToList()) |
| | | 77 | | { |
| | 174 | 78 | | foreach (var handler in entityTypeHandlers) |
| | 58 | 79 | | handler.Handle(this, modelBuilder, entityType); |
| | | 80 | | } |
| | 4 | 81 | | } |
| | | 82 | | |
| | | 83 | | private async Task OnBeforeSavingAsync(CancellationToken cancellationToken) |
| | | 84 | | { |
| | 767 | 85 | | using var scope = ServiceProvider.CreateScope(); |
| | 767 | 86 | | var handlers = scope.ServiceProvider.GetServices<IEntitySavingHandler>().ToList(); |
| | 11960 | 87 | | foreach (var entry in ChangeTracker.Entries().Where(IsModifiedEntity)) |
| | | 88 | | { |
| | 10426 | 89 | | foreach (var handler in handlers) |
| | 0 | 90 | | await handler.HandleAsync(this, entry, cancellationToken); |
| | 5213 | 91 | | } |
| | 767 | 92 | | } |
| | | 93 | | |
| | | 94 | | /// <summary> |
| | | 95 | | /// Determine if an entity was modified. |
| | | 96 | | /// </summary> |
| | | 97 | | private bool IsModifiedEntity(EntityEntry entityEntry) |
| | | 98 | | { |
| | 5213 | 99 | | return ModifiedEntityStates.Contains(entityEntry.State) && entityEntry.Entity is Entity; |
| | | 100 | | } |
| | | 101 | | } |