public void AddEdge_throws_on_verticies_not_in_the_graph() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var edgeOne = new Edge { Id = 1 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertex(vertexOne); Assert.Equal( Strings.GraphDoesNotContainVertex(vertexTwo), Assert.Throws <InvalidOperationException>(() => graph.AddEdge(vertexOne, vertexTwo, edgeOne)).Message); Assert.Equal( Strings.GraphDoesNotContainVertex(vertexTwo), Assert.Throws <InvalidOperationException>(() => graph.AddEdge(vertexTwo, vertexOne, edgeOne)).Message); }
public void AddEdge_updates_incomming_and_outgoing_neighbours() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var edgeThree = new Edge { Id = 3 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree }); graph.AddEdge(vertexOne, vertexTwo, edgeOne); graph.AddEdge(vertexOne, vertexThree, edgeTwo); graph.AddEdge(vertexTwo, vertexThree, edgeThree); Assert.Equal(2, graph.GetOutgoingNeighbours(vertexOne).Count()); Assert.Equal(2, graph.GetOutgoingNeighbours(vertexOne).Intersect(new[] { vertexTwo, vertexThree }).Count()); Assert.Equal(2, graph.GetIncomingNeighbours(vertexThree).Count()); Assert.Equal(2, graph.GetIncomingNeighbours(vertexThree).Intersect(new[] { vertexOne, vertexTwo }).Count()); }
public void TopologicalSort_on_simple_graph_returns_all_verticies_in_order() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree }); // 2-> {1} graph.AddEdge(vertexTwo, vertexOne, edgeOne); // 1 -> {3} graph.AddEdge(vertexOne, vertexThree, edgeTwo); Assert.Equal( new[] { vertexTwo, vertexOne, vertexThree }, graph.TopologicalSort().ToArray()); }
public void AddEdge_adds_an_edge() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo }); graph.AddEdge(vertexOne, vertexTwo, edgeOne); graph.AddEdge(vertexOne, vertexTwo, edgeTwo); Assert.Equal(2, graph.Edges.Count()); Assert.Equal(2, graph.Edges.Intersect(new[] { edgeOne, edgeTwo }).Count()); Assert.Equal(0, graph.GetEdges(vertexTwo, vertexOne).Count()); Assert.Equal(2, graph.GetEdges(vertexOne, vertexTwo).Count()); Assert.Equal(2, graph.GetEdges(vertexOne, vertexTwo).Intersect(new[] { edgeOne, edgeTwo }).Count()); }
public void BatchingTopologicalSort_throws_with_formatted_message_when_cycle_cannot_be_broken() { const string message = "Formatted cycle"; var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var edgeThree = new Edge { Id = 3 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree }); // 1 -> {2} graph.AddEdge(vertexOne, vertexTwo, edgeOne); // 2 -> {3} graph.AddEdge(vertexTwo, vertexThree, edgeTwo); // 3 -> {1} graph.AddEdge(vertexThree, vertexOne, edgeThree); Dictionary <Vertex, Tuple <Vertex, Vertex, IEnumerable <Edge> > > cycleData = null; Func <IEnumerable <Tuple <Vertex, Vertex, IEnumerable <Edge> > >, string> formatter = data => { cycleData = data.ToDictionary(entry => entry.Item1); return(message); }; Assert.Equal( Strings.CircularDependency(message), Assert.Throws <InvalidOperationException>(() => graph.BatchingTopologicalSort(formatter)).Message); Assert.Equal(3, cycleData.Count()); Assert.Equal(vertexTwo, cycleData[vertexOne].Item2); Assert.Equal(new[] { edgeOne }, cycleData[vertexOne].Item3); Assert.Equal(vertexThree, cycleData[vertexTwo].Item2); Assert.Equal(new[] { edgeTwo }, cycleData[vertexTwo].Item3); Assert.Equal(vertexOne, cycleData[vertexThree].Item2); Assert.Equal(new[] { edgeThree }, cycleData[vertexThree].Item3); }
/// <summary> /// Validates the mapping/configuration of the model for cycles. /// </summary> /// <param name="model">The model to validate.</param> /// <param name="logger">The logger to use.</param> protected virtual void ValidateNoCycles( IModel model, IDiagnosticsLogger <DbLoggerCategory.Model.Validation> logger) { var graph = new Multigraph <IEntityType, IForeignKey>(); foreach (var entityType in model.GetEntityTypes()) { var primaryKey = entityType.FindPrimaryKey(); if (primaryKey == null) { continue; } foreach (var foreignKey in entityType.GetForeignKeys()) { var principalType = foreignKey.PrincipalEntityType; if (!foreignKey.PrincipalKey.IsPrimaryKey() || !PropertyListComparer.Instance.Equals(foreignKey.Properties, primaryKey.Properties) || foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)) { continue; } graph.AddVertex(entityType); graph.AddVertex(principalType); graph.AddEdge(entityType, principalType, foreignKey); } } graph.TopologicalSort( tryBreakEdge: null, formatCycle: c => c.Select(d => d.Item1.DisplayName()).Join(" -> "), c => CoreStrings.IdentifyingRelationshipCycle(c)); }
public void TopologicalSort_on_self_ref_can_break_cycle() { var vertexOne = new Vertex { Id = 1 }; var edgeOne = new Edge { Id = 1 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertex(vertexOne); // 1 -> {1} graph.AddEdge(vertexOne, vertexOne, edgeOne); Assert.Equal( new[] { vertexOne }, graph.TopologicalSort( (from, to, edges) => (from == vertexOne) && (to == vertexOne) && (edges.Intersect(new[] { edgeOne }).Count() == 1)).ToArray()); }
private IReadOnlyList<IEntityType> Sort(IReadOnlyList<IEntityType> entityTypes) { var entityTypeGraph = new Multigraph<IEntityType, int>(); entityTypeGraph.AddVertices(entityTypes); foreach (var entityType in entityTypes.Where(et => et.BaseType != null)) { entityTypeGraph.AddEdge(entityType.BaseType, entityType, 0); } return entityTypeGraph.TopologicalSort(); }
private IReadOnlyList<IEntityType> Sort(IReadOnlyList<IEntityType> entityTypes) { var entityTypeGraph = new Multigraph<IEntityType, int>(); entityTypeGraph.AddVertices(entityTypes); foreach (var entityType in entityTypes.Where(et => et.BaseType != null)) { entityTypeGraph.AddEdge(entityType.BaseType, entityType, 0); } return entityTypeGraph.TopologicalSort(); }
public void TopologicalSort_can_break_simple_cycle() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var edgeThree = new Edge { Id = 3 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree }); // 1 -> {2} graph.AddEdge(vertexOne, vertexTwo, edgeOne); // 2 -> {3} graph.AddEdge(vertexTwo, vertexThree, edgeTwo); // 3 -> {1} graph.AddEdge(vertexThree, vertexOne, edgeThree); Assert.Equal( new[] { vertexOne, vertexTwo, vertexThree }, graph.TopologicalSort( (from, to, edges) => from == vertexThree && to == vertexOne && edges.Single() == edgeThree).ToArray()); }
public void TopologicalSort_throws_with_default_message_when_cycle_cannot_be_broken() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var edgeThree = new Edge { Id = 3 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree }); // 1 -> {2} graph.AddEdge(vertexOne, vertexTwo, edgeOne); // 2 -> {3} graph.AddEdge(vertexTwo, vertexThree, edgeTwo); // 3 -> {1} graph.AddEdge(vertexThree, vertexOne, edgeThree); Assert.Equal( CoreStrings.CircularDependency( string.Join( " ->" + Environment.NewLine, new[] { vertexOne, vertexTwo, vertexThree, vertexOne }.Select(v => v.ToString()))), Assert.Throws <InvalidOperationException>(() => graph.TopologicalSort()).Message); }
private static void AddMatchingPredecessorEdge( Dictionary <IKeyValueIndex, List <ModificationCommand> > predecessorsMap, IKeyValueIndex dependentKeyValue, Multigraph <ModificationCommand, IAnnotatable> commandGraph, ModificationCommand command, IForeignKey foreignKey) { if (predecessorsMap.TryGetValue(dependentKeyValue, out var predecessorCommands)) { foreach (var predecessor in predecessorCommands) { if (predecessor != command) { commandGraph.AddEdge(predecessor, command, foreignKey); } } } }
private static void AddMatchingPredecessorEdge <T>( Dictionary <T, List <ModificationCommand> > predecessorsMap, T keyValue, Multigraph <ModificationCommand, IAnnotatable> commandGraph, ModificationCommand command, IAnnotatable edge) where T : notnull { if (predecessorsMap.TryGetValue(keyValue, out var predecessorCommands)) { foreach (var predecessor in predecessorCommands) { if (predecessor != command) { commandGraph.AddEdge(predecessor, command, edge); } } } }
private void AddUniqueValueEdges(Multigraph <ModificationCommand, IAnnotatable> commandGraph) { Dictionary <IIndex, Dictionary <object[], ModificationCommand> > predecessorsMap = null; foreach (var command in commandGraph.Vertices) { if (command.EntityState != EntityState.Modified && command.EntityState != EntityState.Deleted) { continue; } for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++) { var entry = command.Entries[entryIndex]; foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique)) { if (command.EntityState != EntityState.Deleted) { var indexColumnModifications = false; // ReSharper disable once ForCanBeConvertedToForeach // ReSharper disable once LoopCanBeConvertedToQuery for (var indexIndex = 0; indexIndex < command.ColumnModifications.Count; indexIndex++) { var cm = command.ColumnModifications[indexIndex]; if (index.Properties.Contains(cm.Property) && (cm.IsWrite || cm.IsRead)) { indexColumnModifications = true; break; } } if (!indexColumnModifications) { continue; } } var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromOriginalValues( (InternalEntityEntry)entry, out var indexValue)) { predecessorsMap ??= new Dictionary <IIndex, Dictionary <object[], ModificationCommand> >(); if (!predecessorsMap.TryGetValue(index, out var predecessorCommands)) { predecessorCommands = new Dictionary <object[], ModificationCommand>(valueFactory.EqualityComparer); predecessorsMap.Add(index, predecessorCommands); } if (!predecessorCommands.ContainsKey(indexValue)) { predecessorCommands.Add(indexValue, command); } } } } } if (predecessorsMap == null) { return; } foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Modified || command.EntityState == EntityState.Added) { foreach (var entry in command.Entries) { foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique)) { var indexColumnModifications = command.ColumnModifications.Where( cm => index.Properties.Contains(cm.Property) && cm.IsWrite); if (command.EntityState == EntityState.Added || indexColumnModifications.Any()) { var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromCurrentValues((InternalEntityEntry)entry, out var indexValue) && predecessorsMap.TryGetValue(index, out var predecessorCommands) && predecessorCommands.TryGetValue(indexValue, out var predecessor) && predecessor != command) { commandGraph.AddEdge(predecessor, command, index); } } } } } } }
private void AddUniqueValueEdges(Multigraph <ModificationCommand, IAnnotatable> commandGraph) { Dictionary <IIndex, Dictionary <object[], ModificationCommand> >?indexPredecessorsMap = null; var keyPredecessorsMap = new Dictionary <(IKey, IKeyValueIndex), List <ModificationCommand> >(); foreach (var command in commandGraph.Vertices) { if (command.EntityState != EntityState.Modified && command.EntityState != EntityState.Deleted) { continue; } for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++) { var entry = command.Entries[entryIndex]; foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique && i.GetMappedTableIndexes().Any())) { if (entry.EntityState == EntityState.Modified && !index.Properties.Any(p => entry.IsModified(p))) { continue; } var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromOriginalValues(entry, out var indexValue)) { indexPredecessorsMap ??= new Dictionary <IIndex, Dictionary <object[], ModificationCommand> >(); if (!indexPredecessorsMap.TryGetValue(index, out var predecessorCommands)) { predecessorCommands = new Dictionary <object[], ModificationCommand>(valueFactory.EqualityComparer); indexPredecessorsMap.Add(index, predecessorCommands); } if (!predecessorCommands.ContainsKey(indexValue)) { predecessorCommands.Add(indexValue, command); } } } if (command.EntityState != EntityState.Deleted) { continue; } foreach (var key in entry.EntityType.GetKeys().Where(k => k.GetMappedConstraints().Any())) { var principalKeyValue = _keyValueIndexFactorySource .GetKeyValueIndexFactory(key) .CreatePrincipalKeyValue(entry, null); if (principalKeyValue != null) { if (!keyPredecessorsMap.TryGetValue((key, principalKeyValue), out var predecessorCommands)) { predecessorCommands = new List <ModificationCommand>(); keyPredecessorsMap.Add((key, principalKeyValue), predecessorCommands); } predecessorCommands.Add(command); } } } } if (indexPredecessorsMap != null) { foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Deleted) { continue; } foreach (var entry in command.Entries) { foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique && i.GetMappedTableIndexes().Any())) { if (entry.EntityState == EntityState.Modified && !index.Properties.Any(p => entry.IsModified(p))) { continue; } var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromCurrentValues(entry, out var indexValue) && indexPredecessorsMap.TryGetValue(index, out var predecessorCommands) && predecessorCommands.TryGetValue(indexValue, out var predecessor) && predecessor != command) { commandGraph.AddEdge(predecessor, command, index); } } } } } if (keyPredecessorsMap != null) { foreach (var command in commandGraph.Vertices) { if (command.EntityState != EntityState.Added) { continue; } foreach (var entry in command.Entries) { foreach (var key in entry.EntityType.GetKeys().Where(k => k.GetMappedConstraints().Any())) { var principalKeyValue = _keyValueIndexFactorySource .GetKeyValueIndexFactory(key) .CreatePrincipalKeyValue(entry, null); if (principalKeyValue != null) { AddMatchingPredecessorEdge( keyPredecessorsMap, (key, principalKeyValue), commandGraph, command, key); } } } } } }
private void AddUniqueValueEdges(Multigraph <ModificationCommand, IAnnotatable> commandGraph) { Dictionary <IIndex, Dictionary <object[], ModificationCommand> > predecessorsMap = null; foreach (var command in commandGraph.Vertices) { if (command.Predecessor != null) { commandGraph.AddEdge(command.Predecessor, command, null); } if (command.EntityState != EntityState.Modified && command.EntityState != EntityState.Deleted) { continue; } for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++) { var entry = command.Entries[entryIndex]; foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique && i.GetMappedTableIndexes().Any())) { if (command.EntityState == EntityState.Modified && !index.Properties.Any(p => entry.IsModified(p))) { continue; } var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromOriginalValues(entry, out var indexValue)) { predecessorsMap ??= new Dictionary <IIndex, Dictionary <object[], ModificationCommand> >(); if (!predecessorsMap.TryGetValue(index, out var predecessorCommands)) { predecessorCommands = new Dictionary <object[], ModificationCommand>(valueFactory.EqualityComparer); predecessorsMap.Add(index, predecessorCommands); } if (!predecessorCommands.ContainsKey(indexValue)) { predecessorCommands.Add(indexValue, command); } } } } } if (predecessorsMap == null) { return; } foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Modified || command.EntityState == EntityState.Added) { foreach (var entry in command.Entries) { foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique && i.GetMappedTableIndexes().Any())) { if (command.EntityState == EntityState.Modified && !index.Properties.Any(p => entry.IsModified(p))) { continue; } var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromCurrentValues(entry, out var indexValue) && predecessorsMap.TryGetValue(index, out var predecessorCommands) && predecessorCommands.TryGetValue(indexValue, out var predecessor) && predecessor != command) { commandGraph.AddEdge(predecessor, command, index); } } } } } }
private void AddUniqueValueEdges(Multigraph <ModificationCommand, IAnnotatable> commandGraph) { Dictionary <IIndex, Dictionary <object[], ModificationCommand> > predecessorsMap = null; foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Modified || command.EntityState == EntityState.Deleted) { foreach (var entry in command.Entries) { foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique)) { var indexColumnModifications = command.ColumnModifications.Where( cm => index.Properties.Contains(cm.Property) && (cm.IsWrite || cm.IsRead)); if (command.EntityState == EntityState.Deleted || indexColumnModifications.Any()) { var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromOriginalValues((InternalEntityEntry)entry, out var indexValue)) { predecessorsMap = predecessorsMap ?? new Dictionary <IIndex, Dictionary <object[], ModificationCommand> >(); if (!predecessorsMap.TryGetValue(index, out var predecessorCommands)) { predecessorCommands = new Dictionary <object[], ModificationCommand>(valueFactory.EqualityComparer); predecessorsMap.Add(index, predecessorCommands); } predecessorCommands.Add(indexValue, command); } } } } } } if (predecessorsMap == null) { return; } foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Modified || command.EntityState == EntityState.Added) { foreach (var entry in command.Entries) { foreach (var index in entry.EntityType.GetIndexes().Where(i => i.IsUnique)) { var indexColumnModifications = command.ColumnModifications.Where( cm => index.Properties.Contains(cm.Property) && cm.IsWrite); if (command.EntityState == EntityState.Added || indexColumnModifications.Any()) { var valueFactory = index.GetNullableValueFactory <object[]>(); if (valueFactory.TryCreateFromCurrentValues((InternalEntityEntry)entry, out var indexValue) && predecessorsMap.TryGetValue(index, out var predecessorCommands) && predecessorCommands.TryGetValue(indexValue, out var predecessor) && predecessor != command) { commandGraph.AddEdge(predecessor, command, index); } } } } } } }
private static void AddMatchingPredecessorEdge( Dictionary<IKeyValueIndex, List<ModificationCommand>> predecessorsMap, IKeyValueIndex dependentKeyValue, Multigraph<ModificationCommand, IForeignKey> commandGraph, ModificationCommand command, IForeignKey foreignKey) { List<ModificationCommand> predecessorCommands; if (predecessorsMap.TryGetValue(dependentKeyValue, out predecessorCommands)) { foreach (var predecessor in predecessorCommands) { if (predecessor != command) { commandGraph.AddEdge(predecessor, command, foreignKey); } } } }
public void TopologicalSort_can_break_two_cycles() { var vertexOne = new Vertex { Id = 1 }; var vertexTwo = new Vertex { Id = 2 }; var vertexThree = new Vertex { Id = 3 }; var vertexFour = new Vertex { Id = 4 }; var vertexFive = new Vertex { Id = 5 }; var edgeOne = new Edge { Id = 1 }; var edgeTwo = new Edge { Id = 2 }; var edgeThree = new Edge { Id = 3 }; var edgeFour = new Edge { Id = 4 }; var edgeFive = new Edge { Id = 5 }; var edgeSix = new Edge { Id = 6 }; var graph = new Multigraph <Vertex, Edge>(); graph.AddVertices(new[] { vertexOne, vertexTwo, vertexThree, vertexFour, vertexFive }); // 1 -> {2, 4} graph.AddEdge(vertexOne, vertexTwo, edgeOne); graph.AddEdge(vertexOne, vertexFour, edgeTwo); // 2 -> {3} graph.AddEdge(vertexTwo, vertexThree, edgeThree); // 3 -> {1} graph.AddEdge(vertexThree, vertexOne, edgeFour); // 4 -> {5} graph.AddEdge(vertexFour, vertexFive, edgeFive); // 5 -> {1} graph.AddEdge(vertexFive, vertexOne, edgeSix); Assert.Equal( new[] { vertexTwo, vertexThree, vertexOne, vertexFour, vertexFive }, graph.TopologicalSort( (from, to, edges) => { var edge = edges.Single(); return(edge == edgeOne || edge == edgeSix); }).ToArray()); }
protected virtual IReadOnlyList <MigrationOperation> Sort( [NotNull] IEnumerable <MigrationOperation> operations, [NotNull] DiffContext diffContext) { Check.NotNull(operations, nameof(operations)); var dropForeignKeyOperations = new List <MigrationOperation>(); var dropOperations = new List <MigrationOperation>(); var dropColumnOperations = new List <MigrationOperation>(); var dropTableOperations = new List <DropTableOperation>(); var ensureSchemaOperations = new List <MigrationOperation>(); var createSequenceOperations = new List <MigrationOperation>(); var createTableOperations = new List <CreateTableOperation>(); var alterOperations = new List <MigrationOperation>(); var renameOperations = new List <MigrationOperation>(); var renameIndexOperations = new List <MigrationOperation>(); var renameTableOperations = new List <MigrationOperation>(); var leftovers = new List <MigrationOperation>(); foreach (var operation in operations) { var type = operation.GetType(); if (type == typeof(DropForeignKeyOperation)) { dropForeignKeyOperations.Add(operation); } else if (_dropOperationTypes.Contains(type)) { dropOperations.Add(operation); } else if (type == typeof(DropColumnOperation)) { dropColumnOperations.Add(operation); } else if (type == typeof(DropTableOperation)) { dropTableOperations.Add((DropTableOperation)operation); } else if (type == typeof(EnsureSchemaOperation)) { ensureSchemaOperations.Add(operation); } else if (type == typeof(CreateSequenceOperation)) { createSequenceOperations.Add(operation); } else if (type == typeof(CreateTableOperation)) { createTableOperations.Add((CreateTableOperation)operation); } else if (_alterOperationTypes.Contains(type)) { alterOperations.Add(operation); } else if (_renameOperationTypes.Contains(type)) { renameOperations.Add(operation); } else if (type == typeof(RenameIndexOperation)) { renameIndexOperations.Add(operation); } else if (type == typeof(RenameTableOperation)) { renameTableOperations.Add(operation); } else { Debug.Assert(false, "Unexpected operation type: " + operation.GetType()); leftovers.Add(operation); } } var createTableGraph = new Multigraph <CreateTableOperation, AddForeignKeyOperation>(); createTableGraph.AddVertices(createTableOperations); foreach (var createTableOperation in createTableOperations) { foreach (var addForeignKeyOperation in createTableOperation.ForeignKeys) { if ((addForeignKeyOperation.Table == addForeignKeyOperation.PrincipalTable) && (addForeignKeyOperation.Schema == addForeignKeyOperation.PrincipalSchema)) { continue; } var principalCreateTableOperation = createTableOperations.FirstOrDefault( o => (o.Name == addForeignKeyOperation.PrincipalTable) && (o.Schema == addForeignKeyOperation.PrincipalSchema)); if (principalCreateTableOperation != null) { createTableGraph.AddEdge(principalCreateTableOperation, createTableOperation, addForeignKeyOperation); } } } createTableOperations = createTableGraph.TopologicalSort( (principalCreateTableOperation, createTableOperation, addForeignKeyOperations) => { foreach (var addForeignKeyOperation in addForeignKeyOperations) { createTableOperation.ForeignKeys.Remove(addForeignKeyOperation); alterOperations.Add(addForeignKeyOperation); } return(true); }).ToList(); var dropTableGraph = new Multigraph <DropTableOperation, IForeignKey>(); dropTableGraph.AddVertices(dropTableOperations); foreach (var dropTableOperation in dropTableOperations) { var entityType = diffContext.GetMetadata(dropTableOperation); foreach (var foreignKey in GetForeignKeysInHierarchy(entityType)) { var principalRootEntityType = foreignKey.PrincipalEntityType.RootType(); if (entityType == principalRootEntityType) { continue; } var principalDropTableOperation = diffContext.FindDrop(principalRootEntityType); if (principalDropTableOperation != null) { dropTableGraph.AddEdge(dropTableOperation, principalDropTableOperation, foreignKey); } } } var newDiffContext = new DiffContext(); dropTableOperations = dropTableGraph.TopologicalSort( (dropTableOperation, principalDropTableOperation, foreignKeys) => { dropForeignKeyOperations.AddRange(foreignKeys.SelectMany(c => Remove(c, newDiffContext))); return(true); }).ToList(); return(dropForeignKeyOperations .Concat(dropOperations) .Concat(dropColumnOperations) .Concat(dropTableOperations) .Concat(ensureSchemaOperations) .Concat(createSequenceOperations) .Concat(createTableOperations) .Concat(alterOperations) .Concat(renameOperations) .Concat(renameIndexOperations) .Concat(renameTableOperations) .Concat(leftovers) .ToArray()); }
private IEnumerable <IProperty> GetSortedProperties(IEntityType entityType) { var schema = _cassandraOptionsExtension.DefaultKeyspace; var shadowProperties = new List <IProperty>(); var shadowPrimaryKeyProperties = new List <IProperty>(); var primaryKeyPropertyGroups = new Dictionary <PropertyInfo, IProperty>(); var groups = new Dictionary <PropertyInfo, List <IProperty> >(); var unorderedGroups = new Dictionary <PropertyInfo, SortedDictionary <int, IProperty> >(); var types = new Dictionary <Type, SortedDictionary <int, PropertyInfo> >(); var model = entityType.Model; var declaredProperties = entityType.GetDeclaredProperties().ToList(); declaredProperties.AddRange(entityType.ClrType.GetProperties().Where(p => { var et = model.FindEntityType(p.PropertyType); if (et == null || !et.IsUserDefinedType()) { return(false); } return(true); }).Select(p => new Property(p.Name, p.PropertyType, null, null, entityType as EntityType, ConfigurationSource.Convention, null)).ToList()); foreach (var property in declaredProperties) { var clrProperty = property.PropertyInfo; if (clrProperty == null) { if (property.IsPrimaryKey()) { shadowPrimaryKeyProperties.Add(property); continue; } var foreignKey = property.GetContainingForeignKeys() .FirstOrDefault(fk => fk.DependentToPrincipal?.PropertyInfo != null); if (foreignKey == null) { shadowProperties.Add(property); continue; } clrProperty = foreignKey.DependentToPrincipal.PropertyInfo; var groupIndex = foreignKey.Properties.IndexOf(property); unorderedGroups.GetOrAddNew(clrProperty).Add(groupIndex, property); } else { if (property.IsPrimaryKey()) { primaryKeyPropertyGroups.Add(clrProperty, property); } groups.Add( clrProperty, new List <IProperty> { property }); } var clrType = clrProperty.DeclaringType; var index = clrType.GetTypeInfo().DeclaredProperties .IndexOf(clrProperty, PropertyInfoEqualityComparer.Instance); types.GetOrAddNew(clrType)[index] = clrProperty; } foreach (var group in unorderedGroups) { groups.Add(group.Key, group.Value.Values.ToList()); } foreach (var definingForeignKey in entityType.GetDeclaredReferencingForeignKeys() .Where( fk => fk.DeclaringEntityType.GetRootType() != entityType.GetRootType() && fk.DeclaringEntityType.GetTableName() == entityType.GetTableName() && fk.DeclaringEntityType.GetSchema() == schema && fk == fk.DeclaringEntityType .FindForeignKey( fk.DeclaringEntityType.FindPrimaryKey().Properties, entityType.FindPrimaryKey(), entityType))) { var clrProperty = definingForeignKey.PrincipalToDependent?.PropertyInfo; var properties = GetSortedProperties(definingForeignKey.DeclaringEntityType).ToList(); if (clrProperty == null) { shadowProperties.AddRange(properties); continue; } groups.Add(clrProperty, properties); var clrType = clrProperty.DeclaringType; var index = clrType.GetTypeInfo().DeclaredProperties .IndexOf(clrProperty, PropertyInfoEqualityComparer.Instance); types.GetOrAddNew(clrType)[index] = clrProperty; } var graph = new Multigraph <Type, object>(); graph.AddVertices(types.Keys); foreach (var left in types.Keys) { var found = false; foreach (var baseType in left.GetBaseTypes()) { foreach (var right in types.Keys) { if (right == baseType) { graph.AddEdge(right, left, null); found = true; break; } } if (found) { break; } } } var sortedPropertyInfos = graph.TopologicalSort().SelectMany(e => types[e].Values).ToList(); return(sortedPropertyInfos .Select(pi => primaryKeyPropertyGroups.ContainsKey(pi) ? primaryKeyPropertyGroups[pi] : null) .Where(e => e != null) .Concat(shadowPrimaryKeyProperties) .Concat(sortedPropertyInfos.Where(pi => !primaryKeyPropertyGroups.ContainsKey(pi)).SelectMany(p => groups[p])) .Concat(shadowProperties) .Concat(entityType.GetDirectlyDerivedTypes().SelectMany(GetSortedProperties))); }
/// <summary> /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// </summary> protected override IReadOnlyList <MigrationOperation> Sort([NotNull] IEnumerable <MigrationOperation> operations, [NotNull] MigrationsModelDiffer.DiffContext diffContext) { Check.NotNull(operations, nameof(operations)); var dropForeignKeyOperations = new List <MigrationOperation>(); var dropOperations = new List <MigrationOperation>(); var dropColumnOperations = new List <MigrationOperation>(); var dropTableOperations = new List <DropTableOperation>(); var ensureSchemaOperations = new List <MigrationOperation>(); var createSequenceOperations = new List <MigrationOperation>(); var createTableOperations = new List <CreateTableOperation>(); var addColumnOperations = new List <MigrationOperation>(); var alterOperations = new List <MigrationOperation>(); var addForeignKeyOperations = new List <MigrationOperation>(); var renameColumnOperations = new List <MigrationOperation>(); var renameOperations = new List <MigrationOperation>(); var renameTableOperations = new List <MigrationOperation>(); var leftovers = new List <MigrationOperation>(); foreach (var operation in operations) { var type = operation.GetType(); if (type == typeof(DropForeignKeyOperation)) { dropForeignKeyOperations.Add(operation); } else if (_dropOperationTypes.Contains(type)) { dropOperations.Add(operation); } else if (type == typeof(DropColumnOperation)) { dropColumnOperations.Add(operation); } else if (type == typeof(DropTableOperation)) { dropTableOperations.Add((DropTableOperation)operation); } else if (type == typeof(EnsureSchemaOperation)) { ensureSchemaOperations.Add(operation); } else if (type == typeof(CreateSequenceOperation)) { createSequenceOperations.Add(operation); } else if (type == typeof(CreateTableOperation)) { createTableOperations.Add((CreateTableOperation)operation); } else if (type == typeof(AddColumnOperation)) { addColumnOperations.Add(operation); } else if (_alterOperationTypes.Contains(type)) { alterOperations.Add(operation); } else if (type == typeof(AddForeignKeyOperation)) { addForeignKeyOperations.Add(operation); } else if (type == typeof(RenameColumnOperation)) { renameColumnOperations.Add(operation); } else if (_renameOperationTypes.Contains(type)) { renameOperations.Add(operation); } else if (type == typeof(RenameTableOperation)) { renameTableOperations.Add(operation); } else { Debug.Assert(false, "Unexpected operation type: " + operation.GetType()); leftovers.Add(operation); } } var createTableGraph = new Multigraph <CreateTableOperation, AddForeignKeyOperation>(); createTableGraph.AddVertices(createTableOperations); foreach (var createTableOperation in createTableOperations) { foreach (var addForeignKeyOperation in createTableOperation.ForeignKeys) { if ((addForeignKeyOperation.Table == addForeignKeyOperation.PrincipalTable) && (addForeignKeyOperation.Schema == addForeignKeyOperation.PrincipalSchema)) { continue; } var principalCreateTableOperation = createTableOperations.FirstOrDefault( o => (o.Name == addForeignKeyOperation.PrincipalTable) && (o.Schema == addForeignKeyOperation.PrincipalSchema)); if (principalCreateTableOperation != null) { createTableGraph.AddEdge(principalCreateTableOperation, createTableOperation, addForeignKeyOperation); } } } createTableOperations = createTableGraph.TopologicalSort( (principalCreateTableOperation, createTableOperation, cyclicAddForeignKeyOperations) => { foreach (var cyclicAddForeignKeyOperation in cyclicAddForeignKeyOperations) { createTableOperation.ForeignKeys.Remove(cyclicAddForeignKeyOperation); addForeignKeyOperations.Add(cyclicAddForeignKeyOperation); } return(true); }).ToList(); var dropTableGraph = new Multigraph <DropTableOperation, IForeignKey>(); dropTableGraph.AddVertices(dropTableOperations); foreach (var dropTableOperation in dropTableOperations) { var entityType = diffContext.GetMetadata(dropTableOperation); foreach (var foreignKey in (IEnumerable <IForeignKey>)GetForeignKeysInHierarchyMi.Invoke(this, new object[] { entityType })) { var principalRootEntityType = foreignKey.PrincipalEntityType.RootType(); if (entityType == principalRootEntityType) { continue; } var principalDropTableOperation = diffContext.FindDrop(principalRootEntityType); if (principalDropTableOperation != null) { dropTableGraph.AddEdge(dropTableOperation, principalDropTableOperation, foreignKey); } } } var newDiffContext = new DiffContext(); dropTableOperations = dropTableGraph.TopologicalSort( (dropTableOperation, principalDropTableOperation, foreignKeys) => { dropForeignKeyOperations.AddRange(foreignKeys.SelectMany(c => Remove(c, newDiffContext))); return(true); }).ToList(); var ret = dropForeignKeyOperations .Concat(renameTableOperations) .Concat(renameOperations) .Concat(renameColumnOperations) .Concat(dropOperations) .Concat(dropColumnOperations) .Concat(dropTableOperations) .Concat(ensureSchemaOperations) .Concat(createTableOperations) .Concat(addColumnOperations) .Concat(alterOperations) .Concat(addForeignKeyOperations) .Concat(leftovers) .Concat(createSequenceOperations) .ToArray(); // Fixed #37 Renaming a table while adding a new field/column fails when updating the migration in the database. var rename = ret.Where(x => x is RenameTableOperation).ToList(); foreach (RenameTableOperation rto in rename) { var fix = ret.Where(x => !(x is RenameTableOperation)).ToList(); foreach (dynamic x in fix) { try { x.Table = x.Table.Replace(rto.Name, rto.NewName); } catch { } } } // Fixed Renaming a table will drop and re-create PK issue. ret = ret.Where(x => !(x is AddPrimaryKeyOperation && ret.Any(y => y is DropPrimaryKeyOperation && ((DropPrimaryKeyOperation)y).Table == ((AddPrimaryKeyOperation)x).Table)) && !(x is DropPrimaryKeyOperation && ret.Any(y => y is AddPrimaryKeyOperation && ((AddPrimaryKeyOperation)y).Table == ((DropPrimaryKeyOperation)x).Table))).ToArray(); return(ret); }
protected virtual IReadOnlyList <MigrationOperation> Sort([NotNull] IEnumerable <MigrationOperation> operations) { Check.NotNull(operations, nameof(operations)); var dropOperations = new List <MigrationOperation>(); var dropColumnOperations = new List <MigrationOperation>(); var dropTableOperations = new List <MigrationOperation>(); var dropSchemaOperations = new List <MigrationOperation>(); var createSchemaOperations = new List <MigrationOperation>(); var createSequenceOperations = new List <MigrationOperation>(); var createTableOperations = new Dictionary <string, CreateTableOperation>(); var alterOperations = new List <MigrationOperation>(); var renameOperations = new List <MigrationOperation>(); var renameIndexOperations = new List <MigrationOperation>(); var renameTableOperations = new List <MigrationOperation>(); var leftovers = new List <MigrationOperation>(); foreach (var operation in operations) { var type = operation.GetType(); if (_dropOperationTypes.Contains(type)) { dropOperations.Add(operation); } else if (type == typeof(DropColumnOperation)) { dropColumnOperations.Add(operation); } else if (type == typeof(DropTableOperation)) { dropTableOperations.Add(operation); } else if (type == typeof(DropSchemaOperation)) { dropSchemaOperations.Add(operation); } else if (type == typeof(CreateSchemaOperation)) { createSchemaOperations.Add(operation); } else if (type == typeof(CreateSequenceOperation)) { createSequenceOperations.Add(operation); } else if (type == typeof(CreateTableOperation)) { var createTableOperation = (CreateTableOperation)operation; createTableOperations.Add( createTableOperation.Schema + ":" + createTableOperation.Name, createTableOperation); } else if (_alterOperationTypes.Contains(type)) { alterOperations.Add(operation); } else if (_renameOperationTypes.Contains(type)) { renameOperations.Add(operation); } else if (type == typeof(RenameIndexOperation)) { renameIndexOperations.Add(operation); } else if (type == typeof(RenameTableOperation)) { renameTableOperations.Add(operation); } else { Debug.Assert(false, "Unexpected operation type: " + operation.GetType()); leftovers.Add(operation); } } var createTableGraph = new Multigraph <string, AddForeignKeyOperation>(); createTableGraph.AddVertices(createTableOperations.Keys); foreach (var pair in createTableOperations) { foreach (var addForeignKeyOperation in pair.Value.ForeignKeys) { var principalTable = addForeignKeyOperation.ReferencedSchema + ":" + addForeignKeyOperation.ReferencedTable; if (principalTable == pair.Key) { continue; } if (!createTableOperations.ContainsKey(principalTable)) { continue; } createTableGraph.AddEdge(principalTable, pair.Key, addForeignKeyOperation); } } var sortedCreateTableOperations = createTableGraph.TopologicalSort( (principalTable, key, foreignKeyOperations) => { foreach (var foreignKeyOperation in foreignKeyOperations) { createTableOperations[key].ForeignKeys.Remove(foreignKeyOperation); alterOperations.Add(foreignKeyOperation); } return(true); }) .Select(k => createTableOperations[k]); return(dropOperations .Concat(dropColumnOperations) .Concat(dropTableOperations) .Concat(dropSchemaOperations) .Concat(createSchemaOperations) .Concat(createSequenceOperations) .Concat(sortedCreateTableOperations) .Concat(alterOperations) .Concat(renameOperations) .Concat(renameIndexOperations) .Concat(renameTableOperations) .Concat(leftovers) .ToArray()); }
private void AddForeignKeyEdges( Multigraph <ModificationCommand, IForeignKey> commandGraph, Dictionary <KeyValue, List <ModificationCommand> > predecessorsMap) { foreach (var command in commandGraph.Vertices) { if (command.EntityState == EntityState.Modified || command.EntityState == EntityState.Added) { foreach (var entry in command.Entries) { foreach (var foreignKey in entry.EntityType.GetForeignKeys()) { var dependentKeyValue = CreateDependentKeyValue(entry, foreignKey, ValueType.Current); if (dependentKeyValue.Key != EntityKey.InvalidEntityKey) { List <ModificationCommand> predecessorCommands; if (predecessorsMap.TryGetValue(dependentKeyValue, out predecessorCommands)) { foreach (var predecessor in predecessorCommands) { if (predecessor != command) { commandGraph.AddEdge(predecessor, command, foreignKey); } } } } } } } // TODO: also examine modified entities here when principal key modification is supported if (command.EntityState == EntityState.Deleted) { foreach (var entry in command.Entries) { foreach (var foreignKey in entry.EntityType.FindReferencingForeignKeys()) { var principalKeyValue = CreatePrincipalKeyValue(entry.OriginalValues, foreignKey, ValueType.Original); if (principalKeyValue.Key != EntityKey.InvalidEntityKey) { List <ModificationCommand> predecessorCommands; if (predecessorsMap.TryGetValue(principalKeyValue, out predecessorCommands)) { foreach (var predecessor in predecessorCommands) { if (predecessor != command) { commandGraph.AddEdge(predecessor, command, foreignKey); } } } } } } } } }
private void AddForeignKeyEdges( Multigraph <ModificationCommand, IAnnotatable> commandGraph, Dictionary <IKeyValueIndex, List <ModificationCommand> > predecessorsMap) { foreach (var command in commandGraph.Vertices) { if (command.Predecessor != null) { // This is usually an implicit relationship between TPT rows for the same entity commandGraph.AddEdge(command.Predecessor, command, null); } switch (command.EntityState) { case EntityState.Modified: case EntityState.Added: // ReSharper disable once ForCanBeConvertedToForeach for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++) { var entry = command.Entries[entryIndex]; foreach (var foreignKey in entry.EntityType.GetForeignKeys()) { var constraints = foreignKey.GetMappedConstraints(); if (!constraints.Any() || (command.EntityState == EntityState.Modified && !foreignKey.Properties.Any(p => entry.IsModified(p)))) { continue; } var dependentKeyValue = _keyValueIndexFactorySource .GetKeyValueIndexFactory(foreignKey.PrincipalKey) .CreateDependentKeyValue(entry, foreignKey); if (dependentKeyValue == null) { continue; } AddMatchingPredecessorEdge( predecessorsMap, dependentKeyValue, commandGraph, command, foreignKey); } } break; case EntityState.Deleted: // ReSharper disable once ForCanBeConvertedToForeach for (var entryIndex = 0; entryIndex < command.Entries.Count; entryIndex++) { var entry = command.Entries[entryIndex]; foreach (var foreignKey in entry.EntityType.GetReferencingForeignKeys()) { var constraints = foreignKey.GetMappedConstraints() .Where(c => c.PrincipalTable.Name == command.TableName && c.PrincipalTable.Schema == command.Schema); if (!constraints.Any()) { continue; } var principalKeyValue = _keyValueIndexFactorySource .GetKeyValueIndexFactory(foreignKey.PrincipalKey) .CreatePrincipalKeyValueFromOriginalValues(entry, foreignKey); if (principalKeyValue != null) { AddMatchingPredecessorEdge( predecessorsMap, principalKeyValue, commandGraph, command, foreignKey); } } } break; } } }