Advertisement
Guest User

Temporal SqlServer Migrations Sql Generator

a guest
Jan 14th, 2019
277
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.62 KB | None | 0 0
  1. // Rather than augmenting existing SQL it might make more sense to create new operations.
  2. // E.g. MakeTemporal after CreateTable and DropTemporal before DropTable. Not sure how to cause that to happen for now.
  3. public class TemporalSqlServerMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
  4. {
  5.     // HACK: I gave up. This is required to explicitly name history tables and I dont know how to get the target schema from this code.
  6.     private const string DefaultSchema = "dbo";
  7.  
  8.     public TemporalSqlServerMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IMigrationsAnnotationProvider migrationsAnnotations)
  9.         : base(dependencies, migrationsAnnotations) { }
  10.  
  11.     protected override void ColumnDefinition(AddColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
  12.     {
  13.         DisallowNullablePeriodColumns(operation);
  14.         base.ColumnDefinition(operation, model, builder);
  15.     }
  16.  
  17.     protected override void Generate(AlterColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
  18.     {
  19.         DisallowNullablePeriodColumns(operation);
  20.         base.Generate(operation, model, builder);
  21.     }
  22.  
  23.     protected override void Generate(DropColumnOperation operation, IModel model, MigrationCommandListBuilder builder)
  24.     {
  25.         var temporalAnnotation = operation[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?;
  26.         var isPeriodStart = temporalAnnotation == TemporalComponentType.PeriodStart;
  27.         var isPeriodEnd = temporalAnnotation == TemporalComponentType.PeriodEnd;
  28.         if (isPeriodStart || isPeriodEnd)
  29.         {
  30.             // Period columns cant be dropped
  31.             throw new InvalidOperationException("Period Columns cannot be dropped");
  32.         }
  33.  
  34.         base.Generate(operation, model, builder);
  35.     }
  36.  
  37.     protected override void Generate(CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder)
  38.     {
  39.         base.Generate(operation, model, builder);
  40.  
  41.         var temporalAnnotation = operation[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?;
  42.         var isTemporal = temporalAnnotation == TemporalComponentType.Table;
  43.         if (isTemporal)
  44.         {
  45.             var tableName = Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema ?? DefaultSchema);
  46.             var periodStartColumn = operation.Columns.Single(c => (c[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?) == TemporalComponentType.PeriodStart);
  47.             var periodEndColumn = operation.Columns.Single(c => (c[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?) == TemporalComponentType.PeriodEnd);
  48.             var fqPeriodStartColumnName = Dependencies.SqlGenerationHelper.DelimitIdentifier(periodStartColumn.Name);
  49.             var fqPeriodEndColumnName = Dependencies.SqlGenerationHelper.DelimitIdentifier(periodEndColumn.Name);
  50.  
  51.             builder
  52.                 .Append($"ALTER TABLE {tableName} ADD PERIOD FOR SYSTEM_TIME ({fqPeriodStartColumnName}, {fqPeriodEndColumnName})")
  53.                 .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
  54.  
  55.             //TODO: Decide whether we care to make period columns hidden
  56.             builder
  57.                 .Append($"ALTER TABLE {tableName} ALTER COLUMN {fqPeriodStartColumnName} ADD HIDDEN")
  58.                 .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator)
  59.                 .Append($"ALTER TABLE {tableName} ALTER COLUMN {fqPeriodEndColumnName} ADD HIDDEN")
  60.                 .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
  61.  
  62.             //TODO: Do we want to allow custom table suffixes? Use generated history table name?
  63.             var historyTableName = Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name + "_History", operation.Schema ?? DefaultSchema);
  64.  
  65.             builder
  66.                 .Append($"ALTER TABLE {tableName} SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = {historyTableName}))")
  67.                 .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
  68.  
  69.             EndStatement(builder);
  70.         }
  71.     }
  72.  
  73.     protected override void Generate(DropTableOperation operation, IModel model, MigrationCommandListBuilder builder)
  74.     {
  75.         // Cannot drop a System-versioned table so we have to disable it first
  76.         var temporalAnnotation = operation[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?;
  77.         var isTemporal = temporalAnnotation == TemporalComponentType.Table;
  78.         if (isTemporal)
  79.         {
  80.             var fullTableName = Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema ?? DefaultSchema);
  81.  
  82.             builder
  83.                 .Append($"ALTER TABLE {fullTableName} SET (SYSTEM_VERSIONING = OFF)")
  84.                 .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
  85.  
  86.             builder.AppendLine();
  87.         }
  88.  
  89.         base.Generate(operation, model, builder);
  90.     }
  91.  
  92.     private void DisallowNullablePeriodColumns(ColumnOperation operation)
  93.     {
  94.         var temporalAnnotation = operation[TemporalAnnotationNames.TemporalComponent] as TemporalComponentType?;
  95.         var isPeriodStart = temporalAnnotation == TemporalComponentType.PeriodStart;
  96.         var isPeriodEnd = temporalAnnotation == TemporalComponentType.PeriodEnd;
  97.         if (isPeriodStart || isPeriodEnd)
  98.         {
  99.             // Period columns cant be nullable
  100.             if (operation.IsNullable)
  101.             {
  102.                 throw new InvalidOperationException("Period Columns cannot be nullable");
  103.             }
  104.         }
  105.     }
  106. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement