/// <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; }); })); }
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); // 后置处理数据迁移 } }); } }
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(); } }
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); }); } }
/// <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); }
/// <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(); } }
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(); } }
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;