Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrations: Use reflection order for columns in CreateTable #10075

Merged
merged 1 commit into from
Oct 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 122 additions & 1 deletion src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
Expand Down Expand Up @@ -566,7 +567,7 @@ protected virtual IEnumerable<MigrationOperation> Add(
createTableOperation.AddAnnotations(MigrationsAnnotations.For(target.EntityTypes[0]));

createTableOperation.Columns.AddRange(
target.GetProperties().SelectMany(p => Add(p, diffContext, inline: true)).Cast<AddColumnOperation>());
GetSortedProperties(target).SelectMany(p => Add(p, diffContext, inline: true)).Cast<AddColumnOperation>());
var primaryKey = target.EntityTypes[0].FindPrimaryKey();
createTableOperation.PrimaryKey = Add(primaryKey, diffContext).Cast<AddPrimaryKeyOperation>().Single();
createTableOperation.UniqueConstraints.AddRange(
Expand Down Expand Up @@ -605,6 +606,126 @@ protected virtual IEnumerable<MigrationOperation> Remove(
yield return operation;
}

private static IEnumerable<IProperty> GetSortedProperties(TableMapping target)
=> target.EntityTypes
.Where(
t => t.BaseType == null
&& t.FindForeignKeys(t.FindDeclaredPrimaryKey().Properties)
.All(fk => t.Relational().TableName != fk.PrincipalEntityType.Relational().TableName))
.SelectMany(GetSortedProperties)
.Distinct((x, y) => x.Relational().ColumnName == y.Relational().ColumnName);

private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType)
{
var shadowProperties = new List<IProperty>();
var groups = new Dictionary<PropertyInfo, List<IProperty>>();
var unorderedGroups = new Dictionary<PropertyInfo, SortedDictionary<int, IProperty>>();
var types = new Dictionary<Type, SortedDictionary<int, PropertyInfo>>();

foreach (var property in entityType.GetDeclaredProperties())
{
var clrProperty = property.PropertyInfo;
if (clrProperty == null)
{
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
{
groups.Add(clrProperty, new List<IProperty> { property });
}

var clrType = clrProperty.DeclaringType;
var index = clrType.GetTypeInfo().DeclaredProperties
.IndexOf(clrProperty, new PropertyInfoEuqlityComparer());

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.RootType() != entityType.RootType()
&& fk.DeclaringEntityType.Relational().TableName == entityType.Relational().TableName
&& fk == fk.DeclaringEntityType
.FindForeignKey(
fk.DeclaringEntityType.FindDeclaredPrimaryKey().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, new PropertyInfoEuqlityComparer());

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
&& baseType != left)
{
graph.AddEdge(right, left, null);
found = true;

break;
}
}

if (found)
{
break;
}
}
}

return graph.TopologicalSort().SelectMany(t => types[t].Values).SelectMany(p => groups[p])
.Concat(shadowProperties)
.Concat(entityType.GetDirectlyDerivedTypes().SelectMany(GetSortedProperties));
}

private class PropertyInfoEuqlityComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
=> x.IsSameAs(y);

public int GetHashCode(PropertyInfo obj)
=> throw new NotImplementedException();
}

#endregion

#region IProperty
Expand Down
10 changes: 9 additions & 1 deletion src/EFCore/Internal/EnumerableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,16 @@ public static bool StartsWith<TSource>(
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public static int IndexOf<T>([NotNull] this IEnumerable<T> source, [NotNull] T item)
=> IndexOf(source, item, EqualityComparer<T>.Default);

/// <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>
public static int IndexOf<T>([NotNull] this IEnumerable<T> source, [NotNull] T item,
[NotNull] IEqualityComparer<T> comparer)
=> source.Select((x, index) =>
EqualityComparer<T>.Default.Equals(item, x) ? index : -1)
comparer.Equals(item, x) ? index : -1)
.FirstOr(x => x != -1, -1);

/// <summary>
Expand Down
Loading