|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | 3 |
|
| 4 | +using System.Collections; |
4 | 5 | using System.Globalization; |
5 | 6 | using System.Text; |
6 | 7 | using Microsoft.EntityFrameworkCore.SqlServer.Internal; |
@@ -1627,17 +1628,6 @@ protected override void ColumnDefinition( |
1627 | 1628 | var isPeriodStartColumn = operation[SqlServerAnnotationNames.TemporalIsPeriodStartColumn] as bool? == true; |
1628 | 1629 | var isPeriodEndColumn = operation[SqlServerAnnotationNames.TemporalIsPeriodEndColumn] as bool? == true; |
1629 | 1630 |
|
1630 | | - // falling back to legacy annotations, in case the migration was generated using pre-9.0 bits |
1631 | | - if (!isPeriodStartColumn && !isPeriodEndColumn) |
1632 | | - { |
1633 | | - if (operation[SqlServerAnnotationNames.TemporalPeriodStartColumnName] is string periodStartColumnName |
1634 | | - && operation[SqlServerAnnotationNames.TemporalPeriodEndColumnName] is string periodEndColumnName) |
1635 | | - { |
1636 | | - isPeriodStartColumn = operation.Name == periodStartColumnName; |
1637 | | - isPeriodEndColumn = operation.Name == periodEndColumnName; |
1638 | | - } |
1639 | | - } |
1640 | | - |
1641 | 1631 | if (isPeriodStartColumn || isPeriodEndColumn) |
1642 | 1632 | { |
1643 | 1633 | builder.Append(" GENERATED ALWAYS AS ROW "); |
@@ -2391,11 +2381,135 @@ private string Uniquify(string variableName, bool increase = true) |
2391 | 2381 | return _variableCounter == 0 ? variableName : variableName + _variableCounter; |
2392 | 2382 | } |
2393 | 2383 |
|
| 2384 | + private IReadOnlyList<MigrationOperation> FixLegacyTemporalAnnotations(IReadOnlyList<MigrationOperation> migrationOperations) |
| 2385 | + { |
| 2386 | + var resultOperations = new List<MigrationOperation>(); |
| 2387 | + foreach (var migrationOperation in migrationOperations) |
| 2388 | + { |
| 2389 | + var isTemporal = migrationOperation[SqlServerAnnotationNames.IsTemporal] as bool? == true; |
| 2390 | + if (!isTemporal) |
| 2391 | + { |
| 2392 | + resultOperations.Add(migrationOperation); |
| 2393 | + continue; |
| 2394 | + } |
| 2395 | + |
| 2396 | + switch (migrationOperation) |
| 2397 | + { |
| 2398 | + case CreateTableOperation createTableOperation: |
| 2399 | + |
| 2400 | + foreach (var column in createTableOperation.Columns) |
| 2401 | + { |
| 2402 | + NormalizeTemporalAnnotationsForAddColumnOperation(column); |
| 2403 | + } |
| 2404 | + |
| 2405 | + resultOperations.Add(migrationOperation); |
| 2406 | + break; |
| 2407 | + |
| 2408 | + case AddColumnOperation addColumnOperation: |
| 2409 | + NormalizeTemporalAnnotationsForAddColumnOperation(addColumnOperation); |
| 2410 | + resultOperations.Add(addColumnOperation); |
| 2411 | + break; |
| 2412 | + |
| 2413 | + case AlterColumnOperation alterColumnOperation: |
| 2414 | + RemoveLegacyTemporalColumnAnnotations(alterColumnOperation); |
| 2415 | + RemoveLegacyTemporalColumnAnnotations(alterColumnOperation.OldColumn); |
| 2416 | + if (!CanSkipAlterColumnOperation(alterColumnOperation, alterColumnOperation.OldColumn)) |
| 2417 | + { |
| 2418 | + resultOperations.Add(alterColumnOperation); |
| 2419 | + } |
| 2420 | + |
| 2421 | + break; |
| 2422 | + |
| 2423 | + case DropColumnOperation dropColumnOperation: |
| 2424 | + RemoveLegacyTemporalColumnAnnotations(dropColumnOperation); |
| 2425 | + resultOperations.Add(dropColumnOperation); |
| 2426 | + break; |
| 2427 | + |
| 2428 | + case RenameColumnOperation renameColumnOperation: |
| 2429 | + RemoveLegacyTemporalColumnAnnotations(renameColumnOperation); |
| 2430 | + resultOperations.Add(renameColumnOperation); |
| 2431 | + break; |
| 2432 | + |
| 2433 | + default: |
| 2434 | + resultOperations.Add(migrationOperation); |
| 2435 | + break; |
| 2436 | + } |
| 2437 | + } |
| 2438 | + |
| 2439 | + return resultOperations; |
| 2440 | + |
| 2441 | + static void NormalizeTemporalAnnotationsForAddColumnOperation(AddColumnOperation addColumnOperation) |
| 2442 | + { |
| 2443 | + var periodStartColumnName = addColumnOperation[SqlServerAnnotationNames.TemporalPeriodStartColumnName] as string; |
| 2444 | + var periodEndColumnName = addColumnOperation[SqlServerAnnotationNames.TemporalPeriodEndColumnName] as string; |
| 2445 | + if (periodStartColumnName == addColumnOperation.Name) |
| 2446 | + { |
| 2447 | + addColumnOperation.AddAnnotation(SqlServerAnnotationNames.TemporalIsPeriodStartColumn, true); |
| 2448 | + } |
| 2449 | + else if (periodEndColumnName == addColumnOperation.Name) |
| 2450 | + { |
| 2451 | + addColumnOperation.AddAnnotation(SqlServerAnnotationNames.TemporalIsPeriodEndColumn, true); |
| 2452 | + } |
| 2453 | + |
| 2454 | + RemoveLegacyTemporalColumnAnnotations(addColumnOperation); |
| 2455 | + } |
| 2456 | + |
| 2457 | + static void RemoveLegacyTemporalColumnAnnotations(MigrationOperation operation) |
| 2458 | + { |
| 2459 | + operation.RemoveAnnotation(SqlServerAnnotationNames.IsTemporal); |
| 2460 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalHistoryTableName); |
| 2461 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalHistoryTableSchema); |
| 2462 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodStartColumnName); |
| 2463 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodEndColumnName); |
| 2464 | + } |
| 2465 | + |
| 2466 | + static bool CanSkipAlterColumnOperation(ColumnOperation first, ColumnOperation second) |
| 2467 | + => ColumnPropertiesAreTheSame(first, second) && AnnotationsAreTheSame(first, second); |
| 2468 | + |
| 2469 | + // don't compare name, table or schema - they are not being set in the model differ (since they should always be the same) |
| 2470 | + static bool ColumnPropertiesAreTheSame(ColumnOperation first, ColumnOperation second) |
| 2471 | + => first.ClrType == second.ClrType |
| 2472 | + && first.Collation == second.Collation |
| 2473 | + && first.ColumnType == second.ColumnType |
| 2474 | + && first.Comment == second.Comment |
| 2475 | + && first.ComputedColumnSql == second.ComputedColumnSql |
| 2476 | + && Equals(first.DefaultValue, second.DefaultValue) |
| 2477 | + && first.DefaultValueSql == second.DefaultValueSql |
| 2478 | + && first.IsDestructiveChange == second.IsDestructiveChange |
| 2479 | + && first.IsFixedLength == second.IsFixedLength |
| 2480 | + && first.IsNullable == second.IsNullable |
| 2481 | + && first.IsReadOnly == second.IsReadOnly |
| 2482 | + && first.IsRowVersion == second.IsRowVersion |
| 2483 | + && first.IsStored == second.IsStored |
| 2484 | + && first.IsUnicode == second.IsUnicode |
| 2485 | + && first.MaxLength == second.MaxLength |
| 2486 | + && first.Precision == second.Precision |
| 2487 | + && first.Scale == second.Scale; |
| 2488 | + |
| 2489 | + static bool AnnotationsAreTheSame(ColumnOperation first, ColumnOperation second) |
| 2490 | + { |
| 2491 | + var firstAnnotations = first.GetAnnotations().OrderBy(x => x.Name).ToList(); |
| 2492 | + var secondAnnotations = second.GetAnnotations().OrderBy(x => x.Name).ToList(); |
| 2493 | + |
| 2494 | + if (firstAnnotations.Count != secondAnnotations.Count) |
| 2495 | + { |
| 2496 | + return false; |
| 2497 | + } |
| 2498 | + |
| 2499 | + return firstAnnotations.Zip( |
| 2500 | + secondAnnotations, |
| 2501 | + (f, s) => f.Name == s.Name && StructuralComparisons.StructuralEqualityComparer.Equals(f.Value, s.Value)) |
| 2502 | + .All(x => x == true); |
| 2503 | + } |
| 2504 | + } |
| 2505 | + |
2394 | 2506 | private IReadOnlyList<MigrationOperation> RewriteOperations( |
2395 | 2507 | IReadOnlyList<MigrationOperation> migrationOperations, |
2396 | 2508 | IModel? model, |
2397 | 2509 | MigrationsSqlGenerationOptions options) |
2398 | 2510 | { |
| 2511 | + migrationOperations = FixLegacyTemporalAnnotations(migrationOperations); |
| 2512 | + |
2399 | 2513 | var operations = new List<MigrationOperation>(); |
2400 | 2514 | var availableSchemas = new List<string>(); |
2401 | 2515 |
|
|
0 commit comments