예제 #1
0
        /// <summary>
        /// 解析持久化模型。
        /// </summary>
        /// <param name="dbContextAccessor">给定的数据库上下文访问器。</param>
        /// <returns>返回 <see cref="IModel"/>。</returns>
        protected virtual IModel ResolvePersistentModel
            (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            return(ExtensionSettings.Preference.RunLocker(() =>
            {
                var cacheKey = DbContextAccessorHelper.GetMigrationCacheKey(dbContextAccessor);

                return MemoryCache.GetOrCreate(cacheKey, entry =>
                {
                    Type snapshotType = null;

                    // 启用写入分离后,数据库可能会主从同步,因此尝试从数据库获取迁移数据不作连接限制
                    var lastMigration = dbContextAccessor.Migrations
                                        .FirstOrDefaultByMax(s => s.CreatedTimeTicks);

                    if (lastMigration.IsNotNull())
                    {
                        var buffer = ModelSnapshotCompiler.RestoreAssembly(lastMigration.ModelBody);
                        var modelAssembly = Assembly.Load(buffer);

                        snapshotType = modelAssembly.GetType(lastMigration.ModelSnapshotName,
                                                             throwOnError: true, ignoreCase: false);
                    }

                    if (snapshotType.IsNotNull())
                    {
                        return snapshotType.EnsureCreate <ModelSnapshot>().Model;
                    }

                    return null;
                });
            }));
        }
예제 #2
0
        protected virtual void MigrateAspectServices(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                     Action migrateStructureAction)
        {
            IServicesManager <IMigrateAccessorAspect> aspects = null;

            // 数据迁移支持写入连接(包括未启用读写分离的默认连接)// 或启用数据同步的默认与写入连接(数据同步改为在 AccessorBatchExecutor 底层实现)
            if (dbContextAccessor.IsWritingConnectionString()) // || dbContextAccessor.CurrentTenant.DataSynchronization
            {
                aspects = dbContextAccessor.GetService <IServicesManager <IMigrateAccessorAspect> >();
                aspects.ForEach(aspect =>
                {
                    if (aspect.Enabled)
                    {
                        aspect.PreProcess(dbContextAccessor); // 前置处理数据迁移
                    }
                });
            }

            // 结构迁移支持写入连接(包括未启用读写分离的默认连接)或启用结构同步的默认与写入连接
            if (dbContextAccessor.IsWritingConnectionString() || dbContextAccessor.CurrentTenant.StructureSynchronization)
            {
                migrateStructureAction.Invoke();
            }

            if (aspects.IsNotNull())
            {
                aspects.ForEach(aspect =>
                {
                    if (aspect.Enabled)
                    {
                        aspect.PostProcess(dbContextAccessor); // 后置处理数据迁移
                    }
                });
            }
        }
예제 #3
0
        protected virtual async Task MigrateCoreAsync(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                      CancellationToken cancellationToken)
        {
            var lastModel = ResolvePersistentModel(dbContextAccessor);

            if (lastModel.IsNotNull())
            {
                // 对比差异
                var modelDiffer = dbContextAccessor.GetService <IMigrationsModelDiffer>();
                var differences = modelDiffer.GetDifferences(lastModel, dbContextAccessor.Model);
                if (differences.IsNotEmpty())
                {
                    AddMigration(dbContextAccessor);

                    await MigrateAspectServicesAsync(dbContextAccessor, () =>
                    {
                        // 执行差异迁移
                        ExecuteMigrationCommands(dbContextAccessor, differences);
                    },
                                                     cancellationToken).ConfigureAwait();
                }
            }
            else
            {
                await MigrateAspectServicesAsync(dbContextAccessor, async() =>
                {
                    // 初始化整体迁移
                    await dbContextAccessor.Database.MigrateAsync().ConfigureAwait();

                    AddMigration(dbContextAccessor);
                },
                                                 cancellationToken).ConfigureAwait();
            }
        }
예제 #4
0
        protected virtual void MigrateCore(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            var lastModel = ResolvePersistentModel(dbContextAccessor);

            if (lastModel.IsNotNull())
            {
                // 对比差异
                var modelDiffer = dbContextAccessor.GetService <IMigrationsModelDiffer>();
                var differences = modelDiffer.GetDifferences(lastModel, dbContextAccessor.Model);
                if (differences.IsNotEmpty())
                {
                    AddMigration(dbContextAccessor);

                    // 执行差异迁移
                    MigrateAspectServices(dbContextAccessor,
                                          () => ExecuteMigrationCommands(dbContextAccessor, differences));
                }
            }
            else
            {
                // 数据库整体迁移
                MigrateAspectServices(dbContextAccessor, () =>
                {
                    dbContextAccessor.Database.Migrate();

                    AddMigration(dbContextAccessor);
                });
            }
        }
예제 #5
0
        /// <summary>
        /// 获取当前租户核心。
        /// </summary>
        /// <param name="dbContextAccessor">给定的数据库上下文访问器。</param>
        /// <returns>返回 <see cref="ITenant"/>。</returns>
        protected virtual ITenant GetCurrentTenantCore(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            // 默认不切换
            var defaultTenant = Options.DefaultTenant;

            Logger.LogInformation($"Get Default Tenant: Name={defaultTenant?.Name}, Host={defaultTenant?.Host}");

            return(defaultTenant);
        }
예제 #6
0
        /// <summary>
        /// 异步获取当前租户核心。
        /// </summary>
        /// <param name="dbContextAccessor">给定的数据库上下文访问器。</param>
        /// <param name="cancellationToken">给定的 <see cref="CancellationToken"/>。</param>
        /// <returns>返回 <see cref="Task{ITenant}"/>。</returns>
        protected virtual Task <ITenant> GetCurrentTenantCoreAsync(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                                   CancellationToken cancellationToken)
        {
            return(cancellationToken.RunOrCancelAsync(() =>
            {
                // 默认不切换
                var defaultTenant = Options.DefaultTenant;
                Logger.LogInformation($"Get Default Tenant: Name={defaultTenant?.Name}, Host={defaultTenant?.Host}");

                return defaultTenant;
            }));
        }
        protected override void PostProcessCore(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            (var adds, var updates) = GetAddsOrUpdates(dbContextAccessor);

            if (adds.IsNotNull() || updates.IsNotNull())
            {
                var notification = new TabulationNotification <TTabulation>();
                notification.Adds    = adds;
                notification.Updates = updates;

                var mediator = dbContextAccessor.GetService <IMediator>();
                mediator.Publish(notification).ConfigureAwaitCompleted();
            }
        }
예제 #8
0
        protected virtual void AddMigration
            (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            if (!dbContextAccessor.IsWritingConnectionString())
            {
                return;
            }

            (var body, var hash) = CreateModelSnapshot(dbContextAccessor, out var typeName);

            dbContextAccessor.MigrationsManager.TryAdd(p => p.ModelHash == hash,
                                                       () =>
            {
                var identifierGenerator = (IDataStoreIdentificationGenerator <TGenId>)dbContextAccessor
                                          .GetService <IStoreIdentificationGenerator>();

                var migration = ObjectExtensions.EnsureCreate <TMigration>();

                migration.Id = identifierGenerator.GenerateMigrationId();

                migration.PopulateCreation(identifierGenerator.Clock);

                migration.AccessorName      = dbContextAccessor.CurrentType.GetDisplayNameWithNamespace();
                migration.ModelSnapshotName = typeName;
                migration.ModelBody         = body;
                migration.ModelHash         = hash;

                return(migration);
            },
                                                       addedPost =>
            {
                if (!dbContextAccessor.RequiredSaveChanges)
                {
                    dbContextAccessor.RequiredSaveChanges = true;
                }

                // 移除当前缓存
                var cacheKey = DbContextAccessorHelper.GetMigrationCacheKey(dbContextAccessor);
                MemoryCache.Remove(cacheKey);

                // 发送迁移通知
                var mediator = dbContextAccessor.GetService <IMediator>();
                mediator.Publish(new MigrationNotification <TMigration>
                {
                    Migration = addedPost.Entity
                })
                .ConfigureAwaitCompleted();
            });
        }
        protected override async Task PostProcessCoreAsync(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                           CancellationToken cancellationToken = default)
        {
            (var adds, var updates) = GetAddsOrUpdates(dbContextAccessor, cancellationToken);

            if (adds.IsNotNull() || updates.IsNotNull())
            {
                var notification = new TabulationNotification <TTabulation>();
                notification.Adds    = adds;
                notification.Updates = updates;

                var mediator = dbContextAccessor.GetService <IMediator>();
                await mediator.Publish(notification, cancellationToken).ConfigureAwait();
            }
        }
        protected override void PreProcessCore(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
        {
            var entityEntries = GetEntityEntries(dbContextAccessor.ChangeTracker);

            if (entityEntries.IsEmpty())
            {
                return;
            }

            var dictionary = GetAdds(dbContextAccessor, entityEntries);

            if (dictionary.IsNotNull())
            {
                var mediator = dbContextAccessor.GetService <IMediator>();
                mediator.Publish(new AuditNotification <TAudit, TAuditProperty>(dictionary)).ConfigureAwaitCompleted();
            }
        }
예제 #11
0
        protected virtual void ExecuteMigrationCommands(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                        IReadOnlyList <MigrationOperation> operationDifferences)
        {
            //var rawSqlCommandBuilder = dbContextAccessor.GetService<IRawSqlCommandBuilder>();
            var migrationsSqlGenerator   = dbContextAccessor.GetService <IMigrationsSqlGenerator>();
            var migrationCommandExecutor = dbContextAccessor.GetService <IMigrationCommandExecutor>();
            var connection = dbContextAccessor.GetService <IRelationalConnection>();

            //var historyRepository = dbContextAccessor.GetService<IHistoryRepository>();
            //var insertCommand = rawSqlCommandBuilder.Build(historyRepository.GetInsertScript(new HistoryRow(migration.GetId(), ProductInfo.GetVersion())));

            // 过滤已迁移的操作集合(如历史分表迁移)
            operationDifferences = FilterMigratedOperations(dbContextAccessor.TabulationsManager, operationDifferences);
            if (operationDifferences.IsNull())
            {
                return;
            }

            // 按表操作为最高优先级排序
            operationDifferences = operationDifferences.OrderByTableOperation();

            // 生成操作差异的迁移命令集合(如数据库不支持的迁移操作命令)
            var differenceCommands = migrationsSqlGenerator.Generate(operationDifferences, dbContextAccessor.Model);

            //.Concat(new[] { new MigrationCommand(insertCommand, _currentContext.Context, _commandLogger) })
            if (differenceCommands.Count <= 0)
            {
                return;
            }

            // 过滤已执行的迁移命令集合
            var executeCommands = ExecutionValidator.FilterExecuted(dbContextAccessor, differenceCommands);

            if (executeCommands.Count <= 0)
            {
                return;
            }

            ExtensionSettings.Preference.RunLocker(() =>
            {
                migrationCommandExecutor.ExecuteNonQuery(executeCommands, connection);

                ExecutionValidator.SaveExecuted(dbContextAccessor);
            });
        }
        protected override async Task PreProcessCoreAsync(DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
                                                          CancellationToken cancellationToken = default)
        {
            var entityEntries = GetEntityEntries(dbContextAccessor.ChangeTracker);

            if (entityEntries.IsEmpty())
            {
                return;
            }

            var dictionary = GetAdds(dbContextAccessor, entityEntries);

            if (dictionary.IsNotNull())
            {
                var mediator = dbContextAccessor.GetService <IMediator>();
                await mediator.Publish(new AuditNotification <TAudit, TAuditProperty>(dictionary), cancellationToken).ConfigureAwait();
            }
        }
        protected virtual Dictionary <TAudit, List <TAuditProperty> > GetAdds
            (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
            List <EntityEntry> entityEntries)
        {
            var manager = dbContextAccessor.AuditsManager;

            Dictionary <TAudit, List <TAuditProperty> > adds = null;

            // Initialize
            var initialized = manager.TryInitializeRange(() =>
            {
                adds = entityEntries.Select(s => CreateAuditAndProperties(dbContextAccessor.Model, s))
                       .ToDictionary(ks => ks.audit, es => es.auditProperties);

                // 附加属性集合
                foreach (var values in adds.Values)
                {
                    dbContextAccessor.AuditProperties.AddRange(values);
                }

                return(adds.Keys);
            },
                                                         post =>
            {
                if (!dbContextAccessor.RequiredSaveChanges)
                {
                    dbContextAccessor.RequiredSaveChanges = true;
                }
            });

            if (initialized)
            {
                return(adds);
            }

            // Add
            foreach (var entry in entityEntries)
            {
                var entityTableName = GetEntityTableName(entry.Metadata);
                var entityId        = GetEntityId(entry);
                var state           = (int)entry.State;

                manager.TryAdd(p => p.TableName == entityTableName && p.EntityId == entityId && p.State == state,
                               () =>
                {
                    (var audit, var auditProperties) = CreateAuditAndProperties(dbContextAccessor.Model,
                                                                                entry, entityTableName, entityId, state);

                    if (adds.IsNull())
                    {
                        adds = new Dictionary <TAudit, List <TAuditProperty> >();
                    }

                    adds.Add(audit, auditProperties);

                    // 附加属性集合
                    dbContextAccessor.AuditProperties.AddRange(auditProperties);

                    return(audit);
                },
                               addedPost =>
                {
                    if (!dbContextAccessor.RequiredSaveChanges)
                    {
                        dbContextAccessor.RequiredSaveChanges = true;
                    }
                });
            }

            return(adds);
        }
        protected virtual (List <TTabulation> adds, List <TTabulation> updates) GetAddsOrUpdates
            (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
            CancellationToken cancellationToken = default)
        {
            var manager = dbContextAccessor.TabulationsManager;

            List <TTabulation> adds = null;

            // Initialize
            var initialized = manager.TryInitializeRange(() =>
            {
                adds = dbContextAccessor.Model.GetEntityTypes()
                       .Select(s => CreateTabulation(s)).ToList();

                return(adds);
            },
                                                         post =>
            {
                if (!dbContextAccessor.RequiredSaveChanges)
                {
                    dbContextAccessor.RequiredSaveChanges = true;
                }
            });

            if (initialized)
            {
                return(adds, null);
            }

            List <TTabulation> updates = null;

            // AddOrUpdate
            foreach (var entityType in dbContextAccessor.Model.GetEntityTypes())
            {
                var tableName = GetEntityTableName(entityType);
                var schema    = GetEntitySchema(entityType);

                manager.TryAddOrUpdate(p => p.Schema == schema && p.TableName == tableName,
                                       () =>
                {
                    var create = CreateTabulation(entityType, tableName, schema);

                    if (adds.IsNull())
                    {
                        adds = new List <TTabulation>();
                    }

                    adds.Add(create);

                    return(create);
                },
                                       update =>
                {
                    if (IsTabulationUpdated(entityType, update))
                    {
                        if (updates.IsNull())
                        {
                            updates = new List <TTabulation>();
                        }

                        updates.Add(update);
                        return(true);
                    }

                    return(false);
                },
                                       addedOrUpdatedPost =>
                {
                    if (!dbContextAccessor.RequiredSaveChanges)
                    {
                        dbContextAccessor.RequiredSaveChanges = true;
                    }
                });
            }

            return(adds, updates);
        }
 /// <summary>
 /// 前置处理核心。
 /// </summary>
 /// <param name="dbContextAccessor">给定的数据库上下文访问器。</param>
 protected virtual void PreProcessCore
     (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor)
 {
 }
 /// <summary>
 /// 异步后置处理核心。
 /// </summary>
 /// <param name="dbContextAccessor">给定的数据库上下文访问器。</param>
 /// <param name="cancellationToken">给定的 <see cref="CancellationToken"/>(可选)。</param>
 /// <returns>返回 <see cref="Task"/>。</returns>
 protected virtual Task PostProcessCoreAsync
     (DataDbContextAccessor <TAudit, TAuditProperty, TMigration, TTabulation, TTenant, TGenId, TIncremId, TCreatedBy> dbContextAccessor,
     CancellationToken cancellationToken = default)
 => Task.CompletedTask;