Skip to content

Commit

Permalink
Handle entity paths with no tables in model differ (#30347)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajcvickers authored Mar 3, 2023
1 parent 32fe1fd commit da50fcf
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 9 deletions.
20 changes: 11 additions & 9 deletions src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -935,11 +935,13 @@ private static bool ColumnStructureEquals(IColumn source, IColumn target)

private static bool EntityTypePathEquals(IEntityType source, IEntityType target, DiffContext diffContext)
{
var sourceTable = diffContext.GetTable(source);
var targetTable = diffContext.GetTable(target);
var sourceTable = diffContext.FindTable(source);
var targetTable = diffContext.FindTable(target);

if (sourceTable.EntityTypeMappings.Count() == 1
&& targetTable.EntityTypeMappings.Count() == 1)
if ((sourceTable == null
&& targetTable == null)
|| (sourceTable?.EntityTypeMappings.Count() == 1
&& targetTable?.EntityTypeMappings.Count() == 1))
{
return true;
}
Expand All @@ -949,8 +951,8 @@ private static bool EntityTypePathEquals(IEntityType source, IEntityType target,
return false;
}

var nextSource = sourceTable.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType;
var nextTarget = targetTable.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType;
var nextSource = sourceTable?.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType;
var nextTarget = targetTable?.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType;
return (nextSource == null && nextTarget == null)
|| (nextSource != null
&& nextTarget != null
Expand Down Expand Up @@ -1512,7 +1514,7 @@ protected virtual IEnumerable<MigrationOperation> Diff(
Diff,
Add,
Remove,
(s, t, c) => c.GetTable(s.EntityType) == c.FindSource(c.GetTable(t.EntityType))
(s, t, c) => c.FindTable(s.EntityType) == c.FindSource(c.FindTable(t.EntityType))
&& string.Equals(s.Name, t.Name, StringComparison.OrdinalIgnoreCase)
&& string.Equals(s.Sql, t.Sql, StringComparison.OrdinalIgnoreCase));

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

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
140 changes: 140 additions & 0 deletions test/EFCore.SqlServer.Tests/Migrations/SqlServerModelDifferTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,146 @@ public void Alter_column_computation()
Assert.Equal("CAST(CURRENT_TIMESTAMP AS int)", operation.ComputedColumnSql);
});

[ConditionalFact] // Issue #30321
public void Rename_column_TPC()
=> Execute(
source =>
{
source.Entity(
"Campaign",
x =>
{
x.ToTable((string)null);
x.UseTpcMappingStrategy();
x.Property<int>("Id");
x.Property<int>("Status");
});

source.Entity(
"SearchCampaign",
x =>
{
x.HasBaseType("Campaign");
});
},
source =>
{
},
target =>
{
target.Entity(
"Campaign",
x =>
{
x.Property<int>("Status").HasColumnName("status_new");
});
},
operations =>
{
Assert.Equal(1, operations.Count);

var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
Assert.Null(operation.Schema);
Assert.Equal("SearchCampaign", operation.Table);
Assert.Equal("Status", operation.Name);
Assert.Equal("status_new", operation.NewName);
});

[ConditionalFact]
public void Rename_column_TPT()
=> Execute(
source =>
{
source.Entity(
"Campaign",
x =>
{
x.UseTptMappingStrategy();
x.Property<int>("Id");
x.Property<int>("Status");
});

source.Entity(
"SearchCampaign",
x =>
{
x.HasBaseType("Campaign");
});
},
source =>
{
},
target =>
{
target.Entity(
"Campaign",
x =>
{
x.Property<int>("Status").HasColumnName("status_new");
});
},
operations =>
{
Assert.Equal(1, operations.Count);

var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
Assert.Null(operation.Schema);
Assert.Equal("Campaign", operation.Table);
Assert.Equal("Status", operation.Name);
Assert.Equal("status_new", operation.NewName);
});


[ConditionalFact]
public void Rename_column_TPC_non_abstract()
=> Execute(
source =>
{
source.Entity(
"Campaign",
x =>
{
x.UseTpcMappingStrategy();
x.Property<int>("Id");
x.Property<int>("Status");
});

source.Entity(
"SearchCampaign",
x =>
{
x.HasBaseType("Campaign");
});
},
source =>
{
},
target =>
{
target.Entity(
"Campaign",
x =>
{
x.Property<int>("Status").HasColumnName("status_new");
});
},
operations =>
{
Assert.Equal(2, operations.Count);

var operation = Assert.IsType<RenameColumnOperation>(operations[0]);
Assert.Null(operation.Schema);
Assert.Equal("SearchCampaign", operation.Table);
Assert.Equal("Status", operation.Name);
Assert.Equal("status_new", operation.NewName);

operation = Assert.IsType<RenameColumnOperation>(operations[1]);
Assert.Null(operation.Schema);
Assert.Equal("Campaign", operation.Table);
Assert.Equal("Status", operation.Name);
Assert.Equal("status_new", operation.NewName);
});

[ConditionalFact]
public void Alter_primary_key_clustering()
=> Execute(
Expand Down

0 comments on commit da50fcf

Please sign in to comment.