Skip to content

Commit

Permalink
Correctly break double cycles in FK constraints
Browse files Browse the repository at this point in the history
Fixes #22201
  • Loading branch information
AndriySvyryd committed Sep 4, 2020
1 parent a6f0c52 commit 2582851
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,15 @@ protected virtual IReadOnlyList<MigrationOperation> Sort(
{
foreach (var cyclicAddForeignKeyOperation in cyclicAddForeignKeyOperations)
{
createTableOperation.ForeignKeys.Remove(cyclicAddForeignKeyOperation);
constraintOperations.Add(cyclicAddForeignKeyOperation);
var removed = createTableOperation.ForeignKeys.Remove(cyclicAddForeignKeyOperation);
if (removed)
{
constraintOperations.Add(cyclicAddForeignKeyOperation);
}
else
{
Check.DebugAssert(false, "Operation removed twice: " + cyclicAddForeignKeyOperation.ToString());
}
}
return true;
Expand Down
18 changes: 9 additions & 9 deletions src/Shared/Multigraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ internal class Multigraph<TVertex, TEdge> : Graph<TVertex>
private readonly Dictionary<TVertex, Dictionary<TVertex, List<TEdge>>> _successorMap =
new Dictionary<TVertex, Dictionary<TVertex, List<TEdge>>>();

private readonly Dictionary<TVertex, List<TVertex>> _predecessorMap =
new Dictionary<TVertex, List<TVertex>>();
private readonly Dictionary<TVertex, HashSet<TVertex>> _predecessorMap =
new Dictionary<TVertex, HashSet<TVertex>>();

public IEnumerable<TEdge> Edges
=> _successorMap.Values.SelectMany(s => s.Values).SelectMany(e => e).Distinct();
Expand Down Expand Up @@ -71,7 +71,7 @@ public void AddEdge([NotNull] TVertex from, [NotNull] TVertex to, [CanBeNull] TE

if (!_predecessorMap.TryGetValue(to, out var predecessors))
{
predecessors = new List<TVertex>();
predecessors = new HashSet<TVertex>();
_predecessorMap.Add(to, predecessors);
}

Expand Down Expand Up @@ -108,7 +108,7 @@ public void AddEdges([NotNull] TVertex from, [NotNull] TVertex to, [NotNull] IEn

if (!_predecessorMap.TryGetValue(to, out var predecessors))
{
predecessors = new List<TVertex>();
predecessors = new HashSet<TVertex>();
_predecessorMap.Add(to, predecessors);
}

Expand All @@ -126,15 +126,15 @@ public IReadOnlyList<TVertex> TopologicalSort()
=> TopologicalSort(null, null);

public IReadOnlyList<TVertex> TopologicalSort(
[CanBeNull] Func<TVertex, TVertex, IEnumerable<TEdge>, bool> canBreakEdge)
=> TopologicalSort(canBreakEdge, null);
[CanBeNull] Func<TVertex, TVertex, IEnumerable<TEdge>, bool> tryBreakEdge)
=> TopologicalSort(tryBreakEdge, null);

public IReadOnlyList<TVertex> TopologicalSort(
[CanBeNull] Func<IEnumerable<Tuple<TVertex, TVertex, IEnumerable<TEdge>>>, string> formatCycle)
=> TopologicalSort(null, formatCycle);

public IReadOnlyList<TVertex> TopologicalSort(
[CanBeNull] Func<TVertex, TVertex, IEnumerable<TEdge>, bool> canBreakEdge,
[CanBeNull] Func<TVertex, TVertex, IEnumerable<TEdge>, bool> tryBreakEdge,
[CanBeNull] Func<IReadOnlyList<Tuple<TVertex, TVertex, IEnumerable<TEdge>>>, string> formatCycle)
{
var sortedQueue = new List<TVertex>();
Expand Down Expand Up @@ -195,7 +195,7 @@ public IReadOnlyList<TVertex> TopologicalSort(
// Iterate over the unsorted vertices
while ((candidateIndex < candidateVertices.Count)
&& !broken
&& (canBreakEdge != null))
&& tryBreakEdge != null)
{
var candidateVertex = candidateVertices[candidateIndex];

Expand All @@ -206,7 +206,7 @@ public IReadOnlyList<TVertex> TopologicalSort(
foreach (var incomingNeighbor in incomingNeighbors)
{
// Check to see if the edge can be broken
if (canBreakEdge(incomingNeighbor, candidateVertex, _successorMap[incomingNeighbor][candidateVertex]))
if (tryBreakEdge(incomingNeighbor, candidateVertex, _successorMap[incomingNeighbor][candidateVertex]))
{
predecessorCounts[candidateVertex]--;
if (predecessorCounts[candidateVertex] == 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,79 @@ public void Model_differ_breaks_foreign_key_cycles_in_drop_table_operations()
});
}

[ConditionalFact]
public void Model_differ_breaks_double_foreign_key_cycles_in_create_table_operations()
{
Execute(
_ => { },
modelBuilder =>
{
modelBuilder
.Entity("Banks")
.Property<string>("Id");
modelBuilder
.Entity("BankRegistrations")
.Property<string>("Id");
modelBuilder
.Entity("BankProfiles")
.Property<string>("Id");
modelBuilder
.Entity("Banks")
.HasOne("BankRegistrations")
.WithOne()
.HasForeignKey("Banks", "DefaultBankRegistrationId");
modelBuilder
.Entity("Banks")
.HasOne("BankRegistrations")
.WithOne()
.HasForeignKey("Banks", "StagingBankRegistrationId");
modelBuilder
.Entity("Banks")
.HasOne("BankProfiles")
.WithOne()
.HasForeignKey("Banks", "DefaultBankProfileId");
modelBuilder
.Entity("Banks")
.HasOne("BankProfiles")
.WithOne()
.HasForeignKey("Banks", "StagingBankProfileId");
modelBuilder
.Entity("BankRegistrations")
.HasOne("Banks")
.WithMany()
.HasForeignKey("BankId");
modelBuilder
.Entity("BankProfiles")
.HasOne("Banks")
.WithMany()
.HasForeignKey("BankId");
modelBuilder
.Entity("BankProfiles")
.HasOne("BankRegistrations")
.WithMany()
.HasForeignKey("BankRegistrationId");
},
result =>
{
Assert.Equal(14, result.Count);
var createBankTableOperation = Assert.IsType<CreateTableOperation>(result[0]);
Assert.Equal("Banks", createBankTableOperation.Name);
Assert.Equal(0, createBankTableOperation.ForeignKeys.Count);
Assert.Equal(4, result.OfType<AddForeignKeyOperation>().Count());
});
}

[ConditionalFact]
public void Create_table()
{
Expand Down

0 comments on commit 2582851

Please sign in to comment.