|
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;
|
@@ -1599,17 +1600,6 @@ protected override void ColumnDefinition(
|
1599 | 1600 | var isPeriodStartColumn = operation[SqlServerAnnotationNames.TemporalIsPeriodStartColumn] as bool? == true;
|
1600 | 1601 | var isPeriodEndColumn = operation[SqlServerAnnotationNames.TemporalIsPeriodEndColumn] as bool? == true;
|
1601 | 1602 |
|
1602 |
| - // falling back to legacy annotations, in case the migration was generated using pre-9.0 bits |
1603 |
| - if (!isPeriodStartColumn && !isPeriodEndColumn) |
1604 |
| - { |
1605 |
| - if (operation[SqlServerAnnotationNames.TemporalPeriodStartColumnName] is string periodStartColumnName |
1606 |
| - && operation[SqlServerAnnotationNames.TemporalPeriodEndColumnName] is string periodEndColumnName) |
1607 |
| - { |
1608 |
| - isPeriodStartColumn = operation.Name == periodStartColumnName; |
1609 |
| - isPeriodEndColumn = operation.Name == periodEndColumnName; |
1610 |
| - } |
1611 |
| - } |
1612 |
| - |
1613 | 1603 | if (isPeriodStartColumn || isPeriodEndColumn)
|
1614 | 1604 | {
|
1615 | 1605 | builder.Append(" GENERATED ALWAYS AS ROW ");
|
@@ -2363,11 +2353,140 @@ private string Uniquify(string variableName, bool increase = true)
|
2363 | 2353 | return _variableCounter == 0 ? variableName : variableName + _variableCounter;
|
2364 | 2354 | }
|
2365 | 2355 |
|
| 2356 | + private IReadOnlyList<MigrationOperation> FixLegacyTemporalAnnotations(IReadOnlyList<MigrationOperation> migrationOperations) |
| 2357 | + { |
| 2358 | + // short-circuit for non-temporal migrations (which is the majority) |
| 2359 | + if (migrationOperations.All(o => o[SqlServerAnnotationNames.IsTemporal] as bool? != true)) |
| 2360 | + { |
| 2361 | + return migrationOperations; |
| 2362 | + } |
| 2363 | + |
| 2364 | + var resultOperations = new List<MigrationOperation>(migrationOperations.Count); |
| 2365 | + foreach (var migrationOperation in migrationOperations) |
| 2366 | + { |
| 2367 | + var isTemporal = migrationOperation[SqlServerAnnotationNames.IsTemporal] as bool? == true; |
| 2368 | + if (!isTemporal) |
| 2369 | + { |
| 2370 | + resultOperations.Add(migrationOperation); |
| 2371 | + continue; |
| 2372 | + } |
| 2373 | + |
| 2374 | + switch (migrationOperation) |
| 2375 | + { |
| 2376 | + case CreateTableOperation createTableOperation: |
| 2377 | + |
| 2378 | + foreach (var column in createTableOperation.Columns) |
| 2379 | + { |
| 2380 | + NormalizeTemporalAnnotationsForAddColumnOperation(column); |
| 2381 | + } |
| 2382 | + |
| 2383 | + resultOperations.Add(migrationOperation); |
| 2384 | + break; |
| 2385 | + |
| 2386 | + case AddColumnOperation addColumnOperation: |
| 2387 | + NormalizeTemporalAnnotationsForAddColumnOperation(addColumnOperation); |
| 2388 | + resultOperations.Add(addColumnOperation); |
| 2389 | + break; |
| 2390 | + |
| 2391 | + case AlterColumnOperation alterColumnOperation: |
| 2392 | + RemoveLegacyTemporalColumnAnnotations(alterColumnOperation); |
| 2393 | + RemoveLegacyTemporalColumnAnnotations(alterColumnOperation.OldColumn); |
| 2394 | + if (!CanSkipAlterColumnOperation(alterColumnOperation, alterColumnOperation.OldColumn)) |
| 2395 | + { |
| 2396 | + resultOperations.Add(alterColumnOperation); |
| 2397 | + } |
| 2398 | + |
| 2399 | + break; |
| 2400 | + |
| 2401 | + case DropColumnOperation dropColumnOperation: |
| 2402 | + RemoveLegacyTemporalColumnAnnotations(dropColumnOperation); |
| 2403 | + resultOperations.Add(dropColumnOperation); |
| 2404 | + break; |
| 2405 | + |
| 2406 | + case RenameColumnOperation renameColumnOperation: |
| 2407 | + RemoveLegacyTemporalColumnAnnotations(renameColumnOperation); |
| 2408 | + resultOperations.Add(renameColumnOperation); |
| 2409 | + break; |
| 2410 | + |
| 2411 | + default: |
| 2412 | + resultOperations.Add(migrationOperation); |
| 2413 | + break; |
| 2414 | + } |
| 2415 | + } |
| 2416 | + |
| 2417 | + return resultOperations; |
| 2418 | + |
| 2419 | + static void NormalizeTemporalAnnotationsForAddColumnOperation(AddColumnOperation addColumnOperation) |
| 2420 | + { |
| 2421 | + var periodStartColumnName = addColumnOperation[SqlServerAnnotationNames.TemporalPeriodStartColumnName] as string; |
| 2422 | + var periodEndColumnName = addColumnOperation[SqlServerAnnotationNames.TemporalPeriodEndColumnName] as string; |
| 2423 | + if (periodStartColumnName == addColumnOperation.Name) |
| 2424 | + { |
| 2425 | + addColumnOperation.AddAnnotation(SqlServerAnnotationNames.TemporalIsPeriodStartColumn, true); |
| 2426 | + } |
| 2427 | + else if (periodEndColumnName == addColumnOperation.Name) |
| 2428 | + { |
| 2429 | + addColumnOperation.AddAnnotation(SqlServerAnnotationNames.TemporalIsPeriodEndColumn, true); |
| 2430 | + } |
| 2431 | + |
| 2432 | + RemoveLegacyTemporalColumnAnnotations(addColumnOperation); |
| 2433 | + } |
| 2434 | + |
| 2435 | + static void RemoveLegacyTemporalColumnAnnotations(MigrationOperation operation) |
| 2436 | + { |
| 2437 | + operation.RemoveAnnotation(SqlServerAnnotationNames.IsTemporal); |
| 2438 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalHistoryTableName); |
| 2439 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalHistoryTableSchema); |
| 2440 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodStartColumnName); |
| 2441 | + operation.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodEndColumnName); |
| 2442 | + } |
| 2443 | + |
| 2444 | + static bool CanSkipAlterColumnOperation(ColumnOperation column, ColumnOperation oldColumn) |
| 2445 | + => ColumnPropertiesAreTheSame(column, oldColumn) && AnnotationsAreTheSame(column, oldColumn); |
| 2446 | + |
| 2447 | + // don't compare name, table or schema - they are not being set in the model differ (since they should always be the same) |
| 2448 | + static bool ColumnPropertiesAreTheSame(ColumnOperation column, ColumnOperation oldColumn) |
| 2449 | + => column.ClrType == oldColumn.ClrType |
| 2450 | + && column.Collation == oldColumn.Collation |
| 2451 | + && column.ColumnType == oldColumn.ColumnType |
| 2452 | + && column.Comment == oldColumn.Comment |
| 2453 | + && column.ComputedColumnSql == oldColumn.ComputedColumnSql |
| 2454 | + && Equals(column.DefaultValue, oldColumn.DefaultValue) |
| 2455 | + && column.DefaultValueSql == oldColumn.DefaultValueSql |
| 2456 | + && column.IsDestructiveChange == oldColumn.IsDestructiveChange |
| 2457 | + && column.IsFixedLength == oldColumn.IsFixedLength |
| 2458 | + && column.IsNullable == oldColumn.IsNullable |
| 2459 | + && column.IsReadOnly == oldColumn.IsReadOnly |
| 2460 | + && column.IsRowVersion == oldColumn.IsRowVersion |
| 2461 | + && column.IsStored == oldColumn.IsStored |
| 2462 | + && column.IsUnicode == oldColumn.IsUnicode |
| 2463 | + && column.MaxLength == oldColumn.MaxLength |
| 2464 | + && column.Precision == oldColumn.Precision |
| 2465 | + && column.Scale == oldColumn.Scale; |
| 2466 | + |
| 2467 | + static bool AnnotationsAreTheSame(ColumnOperation column, ColumnOperation oldColumn) |
| 2468 | + { |
| 2469 | + var columnAnnotations = column.GetAnnotations().ToList(); |
| 2470 | + var oldColumnAnnotations = oldColumn.GetAnnotations().ToList(); |
| 2471 | + |
| 2472 | + if (columnAnnotations.Count != oldColumnAnnotations.Count) |
| 2473 | + { |
| 2474 | + return false; |
| 2475 | + } |
| 2476 | + |
| 2477 | + return columnAnnotations.Zip(oldColumnAnnotations) |
| 2478 | + .All(x => x.First.Name == x.Second.Name |
| 2479 | + && StructuralComparisons.StructuralEqualityComparer.Equals(x.First.Value, x.Second.Value)); |
| 2480 | + } |
| 2481 | + } |
| 2482 | + |
2366 | 2483 | private IReadOnlyList<MigrationOperation> RewriteOperations(
|
2367 | 2484 | IReadOnlyList<MigrationOperation> migrationOperations,
|
2368 | 2485 | IModel? model,
|
2369 | 2486 | MigrationsSqlGenerationOptions options)
|
2370 | 2487 | {
|
| 2488 | + migrationOperations = FixLegacyTemporalAnnotations(migrationOperations); |
| 2489 | + |
2371 | 2490 | var operations = new List<MigrationOperation>();
|
2372 | 2491 | var availableSchemas = new List<string>();
|
2373 | 2492 |
|
|
0 commit comments