Skip to content

Commit 2013b79

Browse files
authored
[release/7.0] Handle entity paths with no tables in model differ (#30394)
* Handle entity paths with no tables in model differ (#30347) * Add quirk
1 parent 4a42bc8 commit 2013b79

File tree

2 files changed

+155
-9
lines changed

2 files changed

+155
-9
lines changed

src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ public class MigrationsModelDiffer : IMigrationsModelDiffer
1919
private static readonly bool QuirkEnabled27504
2020
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue27504", out var enabled) && enabled;
2121

22+
private static readonly bool UseOldBehavior30321
23+
= AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue30321", out var enabled) && enabled;
24+
2225
private static readonly Type[] DropOperationTypes =
2326
{
2427
typeof(DropIndexOperation),
@@ -939,11 +942,14 @@ private static bool ColumnStructureEquals(IColumn source, IColumn target)
939942

940943
private static bool EntityTypePathEquals(IEntityType source, IEntityType target, DiffContext diffContext)
941944
{
942-
var sourceTable = diffContext.GetTable(source);
943-
var targetTable = diffContext.GetTable(target);
945+
var sourceTable = diffContext.FindTable(source);
946+
var targetTable = diffContext.FindTable(target);
944947

945-
if (sourceTable.EntityTypeMappings.Count() == 1
946-
&& targetTable.EntityTypeMappings.Count() == 1)
948+
if ((!UseOldBehavior30321
949+
&& sourceTable == null
950+
&& targetTable == null)
951+
|| (sourceTable?.EntityTypeMappings.Count() == 1
952+
&& targetTable?.EntityTypeMappings.Count() == 1))
947953
{
948954
return true;
949955
}
@@ -953,8 +959,8 @@ private static bool EntityTypePathEquals(IEntityType source, IEntityType target,
953959
return false;
954960
}
955961

956-
var nextSource = sourceTable.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType;
957-
var nextTarget = targetTable.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType;
962+
var nextSource = sourceTable?.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType;
963+
var nextTarget = targetTable?.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType;
958964
return (nextSource == null && nextTarget == null)
959965
|| (nextSource != null
960966
&& nextTarget != null
@@ -1516,7 +1522,7 @@ protected virtual IEnumerable<MigrationOperation> Diff(
15161522
Diff,
15171523
Add,
15181524
Remove,
1519-
(s, t, c) => c.GetTable(s.EntityType) == c.FindSource(c.GetTable(t.EntityType))
1525+
(s, t, c) => c.FindTable(s.EntityType) == c.FindSource(c.FindTable(t.EntityType))
15201526
&& string.Equals(s.Name, t.Name, StringComparison.OrdinalIgnoreCase)
15211527
&& string.Equals(s.Sql, t.Sql, StringComparison.OrdinalIgnoreCase));
15221528

@@ -2563,8 +2569,8 @@ public virtual void AddDrop(IColumn source, DropColumnOperation operation)
25632569
/// any release. You should only use it directly in your code with extreme caution and knowing that
25642570
/// doing so can result in application failures when updating to a new Entity Framework Core release.
25652571
/// </summary>
2566-
public virtual ITable GetTable(IEntityType entityType)
2567-
=> entityType.GetTableMappings().First().Table;
2572+
public virtual ITable? FindTable(IEntityType entityType)
2573+
=> entityType.GetTableMappings().FirstOrDefault()?.Table;
25682574

25692575
/// <summary>
25702576
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to

test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,146 @@ public void Alter_column_computation()
250250
Assert.Equal("CAST(CURRENT_TIMESTAMP AS int)", operation.ComputedColumnSql);
251251
});
252252

253+
[ConditionalFact] // Issue #30321
254+
public void Rename_column_TPC()
255+
=> Execute(
256+
source =>
257+
{
258+
source.Entity(
259+
"Campaign",
260+
x =>
261+
{
262+
x.ToTable((string)null);
263+
x.UseTpcMappingStrategy();
264+
x.Property<int>("Id");
265+
x.Property<int>("Status");
266+
});
267+
268+
source.Entity(
269+
"SearchCampaign",
270+
x =>
271+
{
272+
x.HasBaseType("Campaign");
273+
});
274+
},
275+
source =>
276+
{
277+
},
278+
target =>
279+
{
280+
target.Entity(
281+
"Campaign",
282+
x =>
283+
{
284+
x.Property<int>("Status").HasColumnName("status_new");
285+
});
286+
},
287+
operations =>
288+
{
289+
Assert.Equal(1, operations.Count);
290+
291+
var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
292+
Assert.Null(operation.Schema);
293+
Assert.Equal("SearchCampaign", operation.Table);
294+
Assert.Equal("Status", operation.Name);
295+
Assert.Equal("status_new", operation.NewName);
296+
});
297+
298+
[ConditionalFact]
299+
public void Rename_column_TPT()
300+
=> Execute(
301+
source =>
302+
{
303+
source.Entity(
304+
"Campaign",
305+
x =>
306+
{
307+
x.UseTptMappingStrategy();
308+
x.Property<int>("Id");
309+
x.Property<int>("Status");
310+
});
311+
312+
source.Entity(
313+
"SearchCampaign",
314+
x =>
315+
{
316+
x.HasBaseType("Campaign");
317+
});
318+
},
319+
source =>
320+
{
321+
},
322+
target =>
323+
{
324+
target.Entity(
325+
"Campaign",
326+
x =>
327+
{
328+
x.Property<int>("Status").HasColumnName("status_new");
329+
});
330+
},
331+
operations =>
332+
{
333+
Assert.Equal(1, operations.Count);
334+
335+
var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
336+
Assert.Null(operation.Schema);
337+
Assert.Equal("Campaign", operation.Table);
338+
Assert.Equal("Status", operation.Name);
339+
Assert.Equal("status_new", operation.NewName);
340+
});
341+
342+
343+
[ConditionalFact]
344+
public void Rename_column_TPC_non_abstract()
345+
=> Execute(
346+
source =>
347+
{
348+
source.Entity(
349+
"Campaign",
350+
x =>
351+
{
352+
x.UseTpcMappingStrategy();
353+
x.Property<int>("Id");
354+
x.Property<int>("Status");
355+
});
356+
357+
source.Entity(
358+
"SearchCampaign",
359+
x =>
360+
{
361+
x.HasBaseType("Campaign");
362+
});
363+
},
364+
source =>
365+
{
366+
},
367+
target =>
368+
{
369+
target.Entity(
370+
"Campaign",
371+
x =>
372+
{
373+
x.Property<int>("Status").HasColumnName("status_new");
374+
});
375+
},
376+
operations =>
377+
{
378+
Assert.Equal(2, operations.Count);
379+
380+
var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
381+
Assert.Null(operation.Schema);
382+
Assert.Equal("SearchCampaign", operation.Table);
383+
Assert.Equal("Status", operation.Name);
384+
Assert.Equal("status_new", operation.NewName);
385+
386+
operation = Assert.IsType<RenameColumnOperation>(operations[1]);
387+
Assert.Null(operation.Schema);
388+
Assert.Equal("Campaign", operation.Table);
389+
Assert.Equal("Status", operation.Name);
390+
Assert.Equal("status_new", operation.NewName);
391+
});
392+
253393
[ConditionalFact]
254394
public void Alter_primary_key_clustering()
255395
=> Execute(

0 commit comments

Comments
 (0)