예제 #1
0
        public void Batch_command_throws_on_commands_with_circular_dependencies()
        {
            var model         = CreateCyclicFKModel();
            var configuration = CreateContextServices(model);
            var stateManager  = configuration.GetRequiredService <IStateManager>();

            var fakeEntry = stateManager.GetOrCreateEntry(new FakeEntity {
                Id = 42, RelatedId = 1
            });

            fakeEntry.SetEntityState(EntityState.Added);

            var relatedFakeEntry = stateManager.GetOrCreateEntry(new RelatedFakeEntity {
                Id = 1, RelatedId = 42
            });

            relatedFakeEntry.SetEntityState(EntityState.Added);

            Assert.Equal(
                CoreStrings.CircularDependency(
                    string.Join(", ",
                                model.GetEntityType(typeof(RelatedFakeEntity)).GetForeignKeys().First(),
                                model.GetEntityType(typeof(FakeEntity)).GetForeignKeys().First())),
                Assert.Throws <InvalidOperationException>(
                    () => { var commandBatches = CreateCommandBatchPreparer().BatchCommands(new[] { fakeEntry, relatedFakeEntry }, new DbContextOptions <DbContext>()).ToArray(); }).Message);
        }
        private IProperty VerifyRootPrincipal(
            IProperty principalProperty,
            Dictionary <IProperty, IProperty> verifiedProperties,
            ImmutableList <IForeignKey> visitedForeignKeys,
            out string errorMessage)
        {
            errorMessage = null;
            IProperty rootPrincipal;

            if (verifiedProperties.TryGetValue(principalProperty, out rootPrincipal))
            {
                return(rootPrincipal);
            }

            var rootPrincipals = new Dictionary <IProperty, IForeignKey>();

            foreach (var foreignKey in principalProperty.DeclaringEntityType.GetForeignKeys())
            {
                for (var index = 0; index < foreignKey.Properties.Count; index++)
                {
                    if (principalProperty == foreignKey.Properties[index])
                    {
                        var nextPrincipalProperty = foreignKey.PrincipalKey.Properties[index];
                        if (visitedForeignKeys.Contains(foreignKey))
                        {
                            var cycleStart = visitedForeignKeys.IndexOf(foreignKey);
                            var cycle      = visitedForeignKeys.GetRange(cycleStart, visitedForeignKeys.Count - cycleStart);
                            errorMessage = Strings.CircularDependency(cycle.Select(fk => fk.ToString()).Join());
                            continue;
                        }
                        rootPrincipal = VerifyRootPrincipal(nextPrincipalProperty, verifiedProperties, visitedForeignKeys.Add(foreignKey), out errorMessage);
                        if (rootPrincipal == null)
                        {
                            if (principalProperty.RequiresValueGenerator)
                            {
                                rootPrincipals[principalProperty] = foreignKey;
                            }
                            continue;
                        }

                        if (principalProperty.RequiresValueGenerator)
                        {
                            ShowError(Strings.ForeignKeyValueGenerationOnAdd(
                                          principalProperty.Name,
                                          principalProperty.DeclaringEntityType.DisplayName(),
                                          Property.Format(foreignKey.Properties)));
                            return(principalProperty);
                        }

                        rootPrincipals[rootPrincipal] = foreignKey;
                    }
                }
            }

            if (rootPrincipals.Count == 0)
            {
                if (errorMessage != null)
                {
                    return(null);
                }

                if (!principalProperty.RequiresValueGenerator)
                {
                    ShowError(Strings.PrincipalKeyNoValueGenerationOnAdd(principalProperty.Name, principalProperty.DeclaringEntityType.DisplayName()));
                    return(null);
                }

                return(principalProperty);
            }

            if (rootPrincipals.Count > 1)
            {
                var firstRoot  = rootPrincipals.Keys.ElementAt(0);
                var secondRoot = rootPrincipals.Keys.ElementAt(1);
                ShowWarning(Strings.MultipleRootPrincipals(
                                rootPrincipals[firstRoot].DeclaringEntityType.DisplayName(),
                                Property.Format(rootPrincipals[firstRoot].Properties),
                                firstRoot.DeclaringEntityType.DisplayName(),
                                firstRoot.Name,
                                Property.Format(rootPrincipals[secondRoot].Properties),
                                secondRoot.DeclaringEntityType.DisplayName(),
                                secondRoot.Name));

                return(firstRoot);
            }

            errorMessage  = null;
            rootPrincipal = rootPrincipals.Keys.Single();
            verifiedProperties[principalProperty] = rootPrincipal;
            return(rootPrincipal);
        }
예제 #3
0
        public virtual IReadOnlyList <TVertex> TopologicalSort(
            [CanBeNull] Func <TVertex, TVertex, IEnumerable <TEdge>, bool> canBreakEdge,
            [CanBeNull] Func <IEnumerable <Tuple <TVertex, TVertex, IEnumerable <TEdge> > >, string> formatCycle)
        {
            var sortedQueue       = new List <TVertex>();
            var predecessorCounts = new Dictionary <TVertex, int>();

            foreach (var vertex in _vertices)
            {
                var count = GetIncomingNeighbours(vertex).Count();
                if (count == 0)
                {
                    // Collect verticies without predecessors
                    sortedQueue.Add(vertex);
                }
                else
                {
                    // Track number of predecessors for remaining verticies
                    predecessorCounts[vertex] = count;
                }
            }

            var index = 0;

            while (sortedQueue.Count < _vertices.Count)
            {
                while (index < sortedQueue.Count)
                {
                    var currentRoot = sortedQueue[index];

                    foreach (var successor in GetOutgoingNeighbours(currentRoot).Where(neighbour => predecessorCounts.ContainsKey(neighbour)))
                    {
                        // Decrement counts for edges from sorted verticies and append any verticies that no longer have predecessors
                        predecessorCounts[successor]--;
                        if (predecessorCounts[successor] == 0)
                        {
                            sortedQueue.Add(successor);
                            predecessorCounts.Remove(successor);
                        }
                    }
                    index++;
                }

                // Cycle breaking
                if (sortedQueue.Count < _vertices.Count)
                {
                    var broken = false;

                    var candidateVertices = predecessorCounts.Keys.ToList();
                    var candidateIndex    = 0;

                    // Iterrate over the unsorted verticies
                    while (candidateIndex < candidateVertices.Count &&
                           !broken &&
                           canBreakEdge != null)
                    {
                        var candidateVertex = candidateVertices[candidateIndex];

                        // Find verticies in the unsorted portion of the graph that have edges to the candidate
                        var incommingNeighbours = GetIncomingNeighbours(candidateVertex)
                                                  .Where(neighbour => predecessorCounts.ContainsKey(neighbour)).ToList();

                        foreach (var incommingNeighbour in incommingNeighbours)
                        {
                            // Check to see if the edge can be broken
                            if (canBreakEdge(incommingNeighbour, candidateVertex, _successorMap[incommingNeighbour][candidateVertex]))
                            {
                                predecessorCounts[candidateVertex]--;
                                if (predecessorCounts[candidateVertex] == 0)
                                {
                                    sortedQueue.Add(candidateVertex);
                                    predecessorCounts.Remove(candidateVertex);
                                    broken = true;
                                    break;
                                }
                            }
                        }
                        candidateIndex++;
                    }
                    if (!broken)
                    {
                        // Failed to break the cycle
                        var currentCycleVertex = predecessorCounts.First().Key;
                        var cycle = new List <TVertex>();
                        cycle.Add(currentCycleVertex);
                        var finished = false;
                        while (!finished)
                        {
                            // Find a cycle
                            foreach (var predecessor in GetIncomingNeighbours(currentCycleVertex)
                                     .Where(neighbour => predecessorCounts.ContainsKey(neighbour)))
                            {
                                if (predecessorCounts[predecessor] != 0)
                                {
                                    predecessorCounts[currentCycleVertex] = -1;

                                    currentCycleVertex = predecessor;
                                    cycle.Add(currentCycleVertex);
                                    finished = predecessorCounts[predecessor] == -1;
                                    break;
                                }
                            }
                        }
                        cycle.Reverse();

                        // Throw an exception
                        if (formatCycle == null)
                        {
                            throw new InvalidOperationException(
                                      Strings.CircularDependency(
                                          cycle.Select(vertex => vertex.ToString()).Join(" -> ")));
                        }
                        // Build the cycle message data
                        currentCycleVertex = cycle.First();
                        var cycleData = new List <Tuple <TVertex, TVertex, IEnumerable <TEdge> > >();

                        foreach (var vertex in cycle.Skip(1))
                        {
                            cycleData.Add(Tuple.Create(currentCycleVertex, vertex, GetEdges(currentCycleVertex, vertex)));
                            currentCycleVertex = vertex;
                        }
                        throw new InvalidOperationException(
                                  Strings.CircularDependency(
                                      formatCycle(cycleData)));
                    }
                }
            }
            return(sortedQueue);
        }
예제 #4
0
        public virtual IReadOnlyList <List <TVertex> > BatchingTopologicalSort(
            [CanBeNull] Func <IEnumerable <Tuple <TVertex, TVertex, IEnumerable <TEdge> > >, string> formatCycle)
        {
            var currentRootsQueue = new List <TVertex>();
            var predecessorCounts = new Dictionary <TVertex, int>();

            foreach (var vertex in _vertices)
            {
                var count = GetIncomingNeighbours(vertex).Count();
                if (count == 0)
                {
                    // Collect verticies without predecessors
                    currentRootsQueue.Add(vertex);
                }
                else
                {
                    // Track number of predecessors for remaining verticies
                    predecessorCounts[vertex] = count;
                }
            }

            var result           = new List <List <TVertex> >();
            var nextRootsQueue   = new List <TVertex>();
            var currentRootIndex = 0;

            while (currentRootIndex < currentRootsQueue.Count)
            {
                var currentRoot = currentRootsQueue[currentRootIndex];
                currentRootIndex++;

                // Remove edges from current root and add any exposed vertices to the next batch
                foreach (var successor in GetOutgoingNeighbours(currentRoot))
                {
                    predecessorCounts[successor]--;
                    if (predecessorCounts[successor] == 0)
                    {
                        nextRootsQueue.Add(successor);
                    }
                }

                // Roll lists over for next batch
                if (currentRootIndex == currentRootsQueue.Count)
                {
                    result.Add(currentRootsQueue);

                    currentRootsQueue = nextRootsQueue;
                    currentRootIndex  = 0;

                    if (currentRootsQueue.Count != 0)
                    {
                        nextRootsQueue = new List <TVertex>();
                    }
                }
            }

            if (result.Sum(b => b.Count) != _vertices.Count)
            {
                // TODO: Support cycle-breaking?

                var currentCycleVertex = predecessorCounts.First(p => p.Value != 0).Key;
                var cycle = new List <TVertex>();
                cycle.Add(currentCycleVertex);
                var finished = false;
                while (!finished)
                {
                    foreach (var predecessor in GetIncomingNeighbours(currentCycleVertex)
                             .Where(neighbour => predecessorCounts.ContainsKey(neighbour)))
                    {
                        if (predecessorCounts[predecessor] != 0)
                        {
                            predecessorCounts[currentCycleVertex] = -1;

                            currentCycleVertex = predecessor;
                            cycle.Add(currentCycleVertex);
                            finished = predecessorCounts[predecessor] == -1;
                            break;
                        }
                    }
                }
                cycle.Reverse();

                // Throw an exception
                if (formatCycle == null)
                {
                    throw new InvalidOperationException(
                              Strings.CircularDependency(
                                  cycle.Select(vertex => vertex.ToString()).Join(" -> ")));
                }
                // Build the cycle message data
                currentCycleVertex = cycle.First();
                var cycleData = new List <Tuple <TVertex, TVertex, IEnumerable <TEdge> > >();

                foreach (var vertex in cycle.Skip(1))
                {
                    cycleData.Add(Tuple.Create(currentCycleVertex, vertex, GetEdges(currentCycleVertex, vertex)));
                    currentCycleVertex = vertex;
                }
                throw new InvalidOperationException(
                          Strings.CircularDependency(
                              formatCycle(cycleData)));
            }

            return(result);
        }