Skip to content

Commit

Permalink
✅ Migrations: Test renames
Browse files Browse the repository at this point in the history
Fixes #1907
  • Loading branch information
bricelam committed Jul 1, 2015
1 parent de5d212 commit e6e77b4
Show file tree
Hide file tree
Showing 4 changed files with 526 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
<Compile Include="Metadata\Conventions\Internal\RelationalConventionSetBuilder.cs" />
<Compile Include="Metadata\Conventions\Internal\RelationalTableAttributeConvention.cs" />
<Compile Include="Metadata\IRelationalMetadataExtensionProvider.cs" />
<Compile Include="Migrations\Infrastructure\ModelDifferContext.cs" />
<Compile Include="RelationalReferenceReferenceBuilderExtensions.cs" />
<Compile Include="Migrations\Builders\ColumnsBuilder.cs" />
<Compile Include="Migrations\Builders\CreateTableBuilder.cs" />
Expand Down
161 changes: 110 additions & 51 deletions src/EntityFramework.Relational/Migrations/Infrastructure/ModelDiffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@

namespace Microsoft.Data.Entity.Migrations.Infrastructure
{
// TODO: Handle transitive renames (See #1907)
// TODO: Structural matching
public class ModelDiffer : IModelDiffer
{
private static readonly Type[] _dropOperationTypes =
{
typeof(DropForeignKeyOperation),
typeof(DropIndexOperation),
typeof(DropPrimaryKeyOperation),
typeof(DropSequenceOperation),
Expand Down Expand Up @@ -72,6 +70,7 @@ protected virtual IReadOnlyList<MigrationOperation> Sort([NotNull] IEnumerable<M
{
Check.NotNull(operations, nameof(operations));

var dropForeignKeyOperations = new List<MigrationOperation>();
var dropOperations = new List<MigrationOperation>();
var dropColumnOperations = new List<MigrationOperation>();
var dropTableOperations = new List<MigrationOperation>();
Expand All @@ -88,7 +87,11 @@ protected virtual IReadOnlyList<MigrationOperation> Sort([NotNull] IEnumerable<M
foreach (var operation in operations)
{
var type = operation.GetType();
if (_dropOperationTypes.Contains(type))
if (type == typeof(DropForeignKeyOperation))
{
dropForeignKeyOperations.Add(operation);
}
else if (_dropOperationTypes.Contains(type))
{
dropOperations.Add(operation);
}
Expand Down Expand Up @@ -173,7 +176,8 @@ protected virtual IReadOnlyList<MigrationOperation> Sort([NotNull] IEnumerable<M
})
.Select(k => createTableOperations[k]);

return dropOperations
return dropForeignKeyOperations
.Concat(dropOperations)
.Concat(dropColumnOperations)
.Concat(dropTableOperations)
.Concat(dropSchemaOperations)
Expand All @@ -190,20 +194,41 @@ protected virtual IReadOnlyList<MigrationOperation> Sort([NotNull] IEnumerable<M

#region IModel

protected virtual IEnumerable<MigrationOperation> Diff([CanBeNull] IModel source, [CanBeNull] IModel target) =>
source != null && target != null
? Diff(source.EntityTypes, target.EntityTypes)
protected virtual IEnumerable<MigrationOperation> Diff([CanBeNull] IModel source, [CanBeNull] IModel target)
{
if (source != null && target != null)
{
var diffContext = new ModelDifferContext();

return Diff(source.EntityTypes, target.EntityTypes, diffContext)
.Concat(
Diff(MetadataExtensions.Extensions(source).Sequences, MetadataExtensions.Extensions(target).Sequences))
: target != null
? Add(target)
: source != null
? Remove(source)
: Enumerable.Empty<MigrationOperation>();
.Concat(
Diff(
source.EntityTypes.SelectMany(t => t.GetForeignKeys()),
target.EntityTypes.SelectMany(t => t.GetForeignKeys()),
diffContext));
}
if (target != null)
{
return Add(target);
}
if (source != null)
{
return Remove(source);
}

protected virtual IEnumerable<MigrationOperation> Add(IModel target) =>
target.EntityTypes.SelectMany(Add)
.Concat(MetadataExtensions.Extensions(target).Sequences.SelectMany(Add));
return Enumerable.Empty<MigrationOperation>();
}

protected virtual IEnumerable<MigrationOperation> Add(IModel target)
{
var diffContext = new ModelDifferContext();

return target.EntityTypes.SelectMany(t => Add(t, diffContext))
.Concat(MetadataExtensions.Extensions(target).Sequences.SelectMany(Add))
.Concat(target.EntityTypes.SelectMany(t => t.GetForeignKeys()).SelectMany(k => Add(k, diffContext)));
}

protected virtual IEnumerable<MigrationOperation> Remove(IModel source) =>
source.EntityTypes.SelectMany(Remove)
Expand All @@ -213,10 +238,16 @@ protected virtual IEnumerable<MigrationOperation> Remove(IModel source) =>

#region IEntityType

protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IEntityType> source, IEnumerable<IEntityType> target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEnumerable<IEntityType> source,
IEnumerable<IEntityType> target,
ModelDifferContext diffContext)
=> DiffCollection(
source, target,
Diff, Add, Remove,
source,
target,
(s, t) => Diff(s, t, diffContext),
t => Add(t, diffContext),
Remove,
(s, t) => string.Equals(s.Name, t.Name, StringComparison.OrdinalIgnoreCase),
(s, t) => string.Equals(
MetadataExtensions.Extensions(s).Schema,
Expand All @@ -231,7 +262,10 @@ protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IEntityType>
MetadataExtensions.Extensions(t).Table,
StringComparison.OrdinalIgnoreCase));

protected virtual IEnumerable<MigrationOperation> Diff(IEntityType source, IEntityType target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEntityType source,
IEntityType target,
ModelDifferContext diffContext)
{
var sourceExtensions = MetadataExtensions.Extensions(source);
var targetExtensions = MetadataExtensions.Extensions(target);
Expand All @@ -249,21 +283,18 @@ protected virtual IEnumerable<MigrationOperation> Diff(IEntityType source, IEnti
};
}

var sourcePrimaryKey = source.GetPrimaryKey();
var targetPrimaryKey = target.GetPrimaryKey();
diffContext.AddMapping(source, target);

var operations = Diff(source.GetProperties(), target.GetProperties())
.Concat(Diff(new[] { sourcePrimaryKey }, new[] { targetPrimaryKey }))
.Concat(Diff(source.GetKeys().Where(k => k != sourcePrimaryKey), target.GetKeys().Where(k => k != targetPrimaryKey)))
.Concat(Diff(source.GetForeignKeys(), target.GetForeignKeys()))
.Concat(Diff(source.GetIndexes(), target.GetIndexes()));
var operations = Diff(source.GetProperties(), target.GetProperties(), diffContext)
.Concat(Diff(source.GetKeys(), target.GetKeys(), diffContext))
.Concat(Diff(source.GetIndexes(), target.GetIndexes(), diffContext));
foreach (var operation in operations)
{
yield return operation;
}
}

protected virtual IEnumerable<MigrationOperation> Add(IEntityType target)
protected virtual IEnumerable<MigrationOperation> Add(IEntityType target, ModelDifferContext diffContext)
{
var targetExtensions = MetadataExtensions.Extensions(target);

Expand All @@ -279,7 +310,8 @@ protected virtual IEnumerable<MigrationOperation> Add(IEntityType target)
createTableOperation.PrimaryKey = Add(primaryKey).Cast<AddPrimaryKeyOperation>().Single();
createTableOperation.UniqueConstraints.AddRange(
target.GetKeys().Where(k => k != primaryKey).SelectMany(Add).Cast<AddUniqueConstraintOperation>());
createTableOperation.ForeignKeys.AddRange(target.GetForeignKeys().SelectMany(Add).Cast<AddForeignKeyOperation>());

diffContext.AddCreate(target, createTableOperation);

yield return createTableOperation;

Expand All @@ -304,10 +336,21 @@ protected virtual IEnumerable<MigrationOperation> Remove(IEntityType source)

#region IProperty

protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IProperty> source, IEnumerable<IProperty> target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEnumerable<IProperty> source,
IEnumerable<IProperty> target,
ModelDifferContext diffContext)
=> DiffCollection(
source, target,
Diff, Add, Remove,
source,
target,
(s, t) =>
{
diffContext.AddMapping(s, t);

return Diff(s, t);
},
Add,
Remove,
(s, t) => string.Equals(s.Name, t.Name, StringComparison.OrdinalIgnoreCase),
(s, t) => string.Equals(
MetadataExtensions.Extensions(s).Column,
Expand Down Expand Up @@ -348,7 +391,7 @@ protected virtual IEnumerable<MigrationOperation> Diff(IProperty source, IProper
|| HasDifferences(Annotations.For(source), targetAnnotations))
{
var isDestructiveChange = (isNullableChanged && source.IsNullable)
// TODO: Detect type narrowing
// TODO: Detect type narrowing
|| columnTypeChanged;

var alterColumnOperation = new AlterColumnOperation
Expand Down Expand Up @@ -405,12 +448,15 @@ protected virtual IEnumerable<MigrationOperation> Remove(IProperty source)

#region IKey

protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IKey> source, IEnumerable<IKey> target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEnumerable<IKey> source,
IEnumerable<IKey> target,
ModelDifferContext diffContext)
=> DiffCollection(
source, target,
Diff, Add, Remove,
(s, t) => MetadataExtensions.Extensions(s).Name == MetadataExtensions.Extensions(t).Name
&& GetColumnNames(s.Properties).SequenceEqual(GetColumnNames(t.Properties))
&& s.Properties.Select(diffContext.FindTarget).SequenceEqual(t.Properties)
&& s.IsPrimaryKey() == t.IsPrimaryKey());

protected virtual IEnumerable<MigrationOperation> Diff(IKey source, IKey target)
Expand Down Expand Up @@ -478,28 +524,30 @@ protected virtual IEnumerable<MigrationOperation> Remove(IKey source)

#region IForeignKey

protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IForeignKey> source, IEnumerable<IForeignKey> target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEnumerable<IForeignKey> source,
IEnumerable<IForeignKey> target,
ModelDifferContext diffContext)
=> DiffCollection(
source, target,
Diff, Add, Remove,
source,
target,
(s, t) => Diff(s, t, diffContext),
t => Add(t, diffContext),
Remove,
(s, t) =>
{
var sourcePrincipalEntityTypeExtensions = MetadataExtensions.Extensions(s.PrincipalEntityType);
var targetPrincipalEntityTypeExtensions = MetadataExtensions.Extensions(t.PrincipalEntityType);

return MetadataExtensions.Extensions(s).Name == MetadataExtensions.Extensions(t).Name
&& GetColumnNames(s.Properties).SequenceEqual(GetColumnNames(t.Properties))
&& sourcePrincipalEntityTypeExtensions.Schema == targetPrincipalEntityTypeExtensions.Schema
&& sourcePrincipalEntityTypeExtensions.Table == targetPrincipalEntityTypeExtensions.Table
&& GetColumnNames(s.PrincipalKey.Properties).SequenceEqual(GetColumnNames(t.PrincipalKey.Properties));
&& s.Properties.Select(diffContext.FindTarget).SequenceEqual(t.Properties)
&& diffContext.FindTarget(s.PrincipalEntityType) == t.PrincipalEntityType
&& s.PrincipalKey.Properties.Select(diffContext.FindTarget).SequenceEqual(t.PrincipalKey.Properties);
});

protected virtual IEnumerable<MigrationOperation> Diff(IForeignKey source, IForeignKey target)
protected virtual IEnumerable<MigrationOperation> Diff(IForeignKey source, IForeignKey target, ModelDifferContext diffContext)
=> HasDifferences(Annotations.For(source), Annotations.For(target))
? Remove(source).Concat(Add(target))
? Remove(source).Concat(Add(target, diffContext))
: Enumerable.Empty<MigrationOperation>();

protected virtual IEnumerable<MigrationOperation> Add(IForeignKey target)
protected virtual IEnumerable<MigrationOperation> Add(IForeignKey target, ModelDifferContext diffContext)
{
var targetExtensions = MetadataExtensions.Extensions(target);
var targetEntityTypeExtensions = MetadataExtensions.Extensions(target.EntityType);
Expand All @@ -518,7 +566,15 @@ protected virtual IEnumerable<MigrationOperation> Add(IForeignKey target)
};
CopyAnnotations(Annotations.For(target), operation);

yield return operation;
var createTableOperation = diffContext.FindCreate(target.EntityType);
if (createTableOperation != null)
{
createTableOperation.ForeignKeys.Add(operation);
}
else
{
yield return operation;
}
}

protected virtual IEnumerable<MigrationOperation> Remove(IForeignKey source)
Expand All @@ -538,16 +594,19 @@ protected virtual IEnumerable<MigrationOperation> Remove(IForeignKey source)

#region IIndex

protected virtual IEnumerable<MigrationOperation> Diff(IEnumerable<IIndex> source, IEnumerable<IIndex> target)
protected virtual IEnumerable<MigrationOperation> Diff(
IEnumerable<IIndex> source,
IEnumerable<IIndex> target,
ModelDifferContext diffContext)
=> DiffCollection(
source, target,
Diff, Add, Remove,
(s, t) => string.Equals(
MetadataExtensions.Extensions(s).Name,
MetadataExtensions.Extensions(t).Name,
StringComparison.OrdinalIgnoreCase)
&& GetColumnNames(s.Properties).SequenceEqual(GetColumnNames(t.Properties)),
(s, t) => GetColumnNames(s.Properties).SequenceEqual(GetColumnNames(t.Properties)));
&& s.Properties.Select(diffContext.FindTarget).SequenceEqual(t.Properties),
(s, t) => s.Properties.Select(diffContext.FindTarget).SequenceEqual(t.Properties));

protected virtual IEnumerable<MigrationOperation> Diff(IIndex source, IIndex target)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Metadata;
using Microsoft.Data.Entity.Migrations.Operations;

namespace Microsoft.Data.Entity.Migrations.Infrastructure
{
public class ModelDifferContext
{
private readonly IDictionary<IEntityType, IEntityType> _entityTypeMap = new Dictionary<IEntityType, IEntityType>();
private readonly IDictionary<IProperty, IProperty> _propertyMap = new Dictionary<IProperty, IProperty>();
private readonly IDictionary<IEntityType, CreateTableOperation> _createTableOperations = new Dictionary<IEntityType, CreateTableOperation>();

public virtual void AddMapping([NotNull] IEntityType source, [NotNull] IEntityType target)
=> _entityTypeMap.Add(source, target);
public virtual void AddMapping([NotNull] IProperty source, [NotNull] IProperty target)
=> _propertyMap.Add(source, target);
public virtual void AddCreate([NotNull] IEntityType target, [NotNull] CreateTableOperation operation)
=> _createTableOperations.Add(target, operation);

public virtual IEntityType FindTarget([NotNull] IEntityType source)
{
IEntityType target;
_entityTypeMap.TryGetValue(source, out target);

return target;
}

public virtual IProperty FindTarget([NotNull] IProperty source)
{
IProperty target;
_propertyMap.TryGetValue(source, out target);

return target;
}

public virtual CreateTableOperation FindCreate([NotNull] IEntityType target)
{
CreateTableOperation operation;
_createTableOperations.TryGetValue(target, out operation);

return operation;
}
}
}
Loading

0 comments on commit e6e77b4

Please sign in to comment.