Skip to content

Commit

Permalink
Refactor relational overridable metadata API
Browse files Browse the repository at this point in the history
Fixes #21415
  • Loading branch information
AndriySvyryd committed Jul 6, 2020
1 parent dff6a1a commit 7f0a8d1
Show file tree
Hide file tree
Showing 44 changed files with 738 additions and 1,359 deletions.
24 changes: 0 additions & 24 deletions src/EFCore.Relational/Design/AnnotationCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,20 +89,6 @@ public virtual void RemoveAnnotationsHandledByConventions(
annotations.Remove(RelationalAnnotationNames.ColumnName);
}

if (annotations.TryGetValue(RelationalAnnotationNames.ViewColumnName, out var viewColumnNameAnnotation)
&& viewColumnNameAnnotation.Value is string viewColumnName
&& viewColumnName == columnName)
{
annotations.Remove(RelationalAnnotationNames.ViewColumnName);
}

if (annotations.TryGetValue(RelationalAnnotationNames.FunctionColumnName, out var functionColumnNameAnnotation)
&& functionColumnNameAnnotation.Value is string functionColumnName
&& functionColumnName == columnName)
{
annotations.Remove(RelationalAnnotationNames.FunctionColumnName);
}

RemoveConventionalAnnotationsHelper(property, annotations, IsHandledByConvention);
}

Expand Down Expand Up @@ -159,16 +145,6 @@ public virtual IReadOnlyList<MethodCallCodeFragment> GenerateFluentApiCalls(
annotations,
RelationalAnnotationNames.ColumnName, nameof(RelationalPropertyBuilderExtensions.HasColumnName), methodCallCodeFragments);

GenerateSimpleFluentApiCall(
annotations,
RelationalAnnotationNames.ViewColumnName, nameof(RelationalPropertyBuilderExtensions.HasViewColumnName),
methodCallCodeFragments);

GenerateSimpleFluentApiCall(
annotations,
RelationalAnnotationNames.FunctionColumnName, nameof(RelationalPropertyBuilderExtensions.HasFunctionColumnName),
methodCallCodeFragments);

GenerateSimpleFluentApiCall(
annotations,
RelationalAnnotationNames.DefaultValueSql, nameof(RelationalPropertyBuilderExtensions.HasDefaultValueSql),
Expand Down
3 changes: 1 addition & 2 deletions src/EFCore.Relational/Diagnostics/DbCommandMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Diagnostics
{
/// <summary>
/// Enum used by <see cref="CommandEventData" />, an subclasses to indicate the
/// method on <see cref="DbCommand" />
/// being used to execute the command.
/// method on <see cref="DbCommand" /> being used to execute the command.
/// </summary>
public enum DbCommandMethod
{
Expand Down
91 changes: 61 additions & 30 deletions src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,44 +619,75 @@ public static string SetComment(
/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same table.
/// </summary>
public static IEnumerable<IForeignKey> FindTableRowInternalForeignKeys(
[NotNull] this IEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.Table);
/// <param name="entityType"> The entity type. </param>
/// <param name="storeObject"> The identifier of the store object. </param>
public static IEnumerable<IForeignKey> FindRowInternalForeignKeys(
[NotNull] this IEntityType entityType, StoreObjectIdentifier storeObject)
{
var primaryKey = entityType.FindPrimaryKey();
if (primaryKey == null)
{
yield break;
}

/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same table.
/// </summary>
public static IEnumerable<IMutableForeignKey> FindTableRowInternalForeignKeys(
[NotNull] this IMutableEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.Table).Cast<IMutableForeignKey>();
foreach (var fk in entityType.GetForeignKeys())
{
var principalEntityType = fk.PrincipalEntityType;
if (!fk.PrincipalKey.IsPrimaryKey()
|| principalEntityType == fk.DeclaringEntityType
|| !fk.IsUnique
#pragma warning disable EF1001 // Internal EF Core API usage.
|| !PropertyListComparer.Instance.Equals(fk.Properties, primaryKey.Properties))
#pragma warning restore EF1001 // Internal EF Core API usage.
{
continue;
}

switch (storeObject.StoreObjectType)
{
case StoreObjectType.Table:
if (storeObject.Name == principalEntityType.GetTableName()
&& storeObject.Schema == principalEntityType.GetSchema())
{
yield return fk;
}
break;
case StoreObjectType.View:
if (storeObject.Name == principalEntityType.GetViewName()
&& storeObject.Schema == principalEntityType.GetViewSchema())
{
yield return fk;
}
break;
case StoreObjectType.Function:
if (storeObject.Name == principalEntityType.GetFunctionName())
{
yield return fk;
}
break;
default:
throw new NotImplementedException(storeObject.StoreObjectType.ToString());
}
}
}

/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same table.
/// </summary>
public static IEnumerable<IConventionForeignKey> FindTableRowInternalForeignKeys(
[NotNull] this IConventionEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.Table).Cast<IConventionForeignKey>();

/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same view.
/// </summary>
public static IEnumerable<IForeignKey> FindViewRowInternalForeignKeys(
[NotNull] this IEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.View);

/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same view.
/// </summary>
public static IEnumerable<IMutableForeignKey> FindViewRowInternalForeignKeys(
[NotNull] this IMutableEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.View).Cast<IMutableForeignKey>();
/// <param name="entityType"> The entity type. </param>
/// <param name="storeObject"> The identifier of the store object. </param>
public static IEnumerable<IMutableForeignKey> FindRowInternalForeignKeys(
[NotNull] this IMutableEntityType entityType, StoreObjectIdentifier storeObject)
=> ((IEntityType)entityType).FindRowInternalForeignKeys(storeObject).Cast<IMutableForeignKey>();

/// <summary>
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same view.
/// Gets the foreign keys for the given entity type that point to other entity types sharing the same table.
/// </summary>
public static IEnumerable<IConventionForeignKey> FindViewRowInternalForeignKeys(
[NotNull] this IConventionEntityType entityType, [NotNull] string name, [CanBeNull] string schema)
=> entityType.FindRowInternalForeignKeys(name, schema, StoreObjectType.View).Cast<IConventionForeignKey>();
/// <param name="entityType"> The entity type. </param>
/// <param name="storeObject"> The identifier of the store object. </param>
public static IEnumerable<IConventionForeignKey> FindRowInternalForeignKeys(
[NotNull] this IConventionEntityType entityType, StoreObjectIdentifier storeObject)
=> ((IEntityType)entityType).FindRowInternalForeignKeys(storeObject).Cast<IConventionForeignKey>();

/// <summary>
/// Gets a value indicating whether the associated table is ignored by Migrations.
Expand Down
41 changes: 14 additions & 27 deletions src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,22 @@ public static string GetDefaultName(
[NotNull] string principalTableName,
[CanBeNull] string principalSchema)
{
var propertyNames = foreignKey.Properties.Select(p => p.GetColumnName(tableName, schema)).ToList();
var principalPropertyNames = foreignKey.PrincipalKey.Properties.Select(p => p.GetColumnName(tableName, schema)).ToList();
var storeObject = StoreObjectIdentifier.Table(tableName, schema);
var propertyNames = foreignKey.Properties.Select(p => p.GetColumnName(storeObject)).ToList();
var principalPropertyNames = foreignKey.PrincipalKey.Properties.Select(p => p.GetColumnName(storeObject)).ToList();
var rootForeignKey = foreignKey;

// Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
var linkedForeignKey = rootForeignKey.DeclaringEntityType
.FindTableRowInternalForeignKeys(tableName, schema)
.FindRowInternalForeignKeys(storeObject)
.SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())
.FirstOrDefault(k => principalTableName == k.PrincipalEntityType.GetTableName()
&& principalSchema == k.PrincipalEntityType.GetSchema()
&& propertyNames.SequenceEqual(k.Properties.Select(p => p.GetColumnName(tableName, schema)))
&& principalPropertyNames.SequenceEqual(k.PrincipalKey.Properties.Select(p => p.GetColumnName(tableName, schema))));
&& propertyNames.SequenceEqual(k.Properties.Select(p => p.GetColumnName(storeObject)))
&& principalPropertyNames.SequenceEqual(k.PrincipalKey.Properties.Select(p => p.GetColumnName(storeObject))));
if (linkedForeignKey == null)
{
break;
Expand All @@ -123,7 +124,7 @@ public static string GetDefaultName(
.Append("_")
.Append(principalTableName)
.Append("_")
.AppendJoin(foreignKey.Properties.Select(p => p.GetColumnName(tableName, schema)), "_")
.AppendJoin(foreignKey.Properties.Select(p => p.GetColumnName(storeObject)), "_")
.ToString();

return Uniquifier.Truncate(baseName, foreignKey.DeclaringEntityType.Model.GetMaxIdentifierLength());
Expand Down Expand Up @@ -187,30 +188,26 @@ public static IEnumerable<IForeignKeyConstraint> GetMappedConstraints([NotNull]
/// <param name="foreignKey"> The foreign key. </param>
/// <param name="tableName"> The table name. </param>
/// <param name="schema"> The schema. </param>
/// <param name="principalTableName"> The principal table name. </param>
/// <param name="principalSchema"> The principal schema. </param>
/// <returns> The foreign key if found, or <see langword="null" /> if none was found.</returns>
public static IForeignKey FindSharedTableRootForeignKey(
[NotNull] this IForeignKey foreignKey,
[NotNull] string tableName,
[CanBeNull] string schema,
[NotNull] string principalTableName,
[CanBeNull] string principalSchema)
[CanBeNull] string schema)
{
Check.NotNull(foreignKey, nameof(foreignKey));
Check.NotNull(tableName, nameof(tableName));
Check.NotNull(principalTableName, nameof(principalTableName));

var foreignKeyName = foreignKey.GetConstraintName(tableName, schema,
foreignKey.PrincipalEntityType.GetTableName(), foreignKey.PrincipalEntityType.GetSchema());
var rootForeignKey = foreignKey;

var storeObject = StoreObjectIdentifier.Table(tableName, schema);
// Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
var linkedKey = rootForeignKey.DeclaringEntityType
.FindTableRowInternalForeignKeys(tableName, schema)
.FindRowInternalForeignKeys(storeObject)
.SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())
.FirstOrDefault(k => k.GetConstraintName(tableName, schema,
k.PrincipalEntityType.GetTableName(), k.PrincipalEntityType.GetSchema())
Expand Down Expand Up @@ -238,17 +235,12 @@ public static IForeignKey FindSharedTableRootForeignKey(
/// <param name="foreignKey"> The foreign key. </param>
/// <param name="tableName"> The table name. </param>
/// <param name="schema"> The schema. </param>
/// <param name="principalTableName"> The principal table name. </param>
/// <param name="principalSchema"> The principal schema. </param>
/// <returns> The foreign key if found, or <see langword="null" /> if none was found.</returns>
public static IMutableForeignKey FindSharedTableRootForeignKey(
[NotNull] this IMutableForeignKey foreignKey,
[NotNull] string tableName,
[CanBeNull] string schema,
[NotNull] string principalTableName,
[CanBeNull] string principalSchema)
=> (IMutableForeignKey)((IForeignKey)foreignKey).FindSharedTableRootForeignKey(
tableName, schema, principalTableName, principalSchema);
[CanBeNull] string schema)
=> (IMutableForeignKey)((IForeignKey)foreignKey).FindSharedTableRootForeignKey(tableName, schema);

/// <summary>
/// <para>
Expand All @@ -262,16 +254,11 @@ public static IMutableForeignKey FindSharedTableRootForeignKey(
/// <param name="foreignKey"> The foreign key. </param>
/// <param name="tableName"> The table name. </param>
/// <param name="schema"> The schema. </param>
/// <param name="principalTableName"> The principal table name. </param>
/// <param name="principalSchema"> The principal schema. </param>
/// <returns> The foreign key if found, or <see langword="null" /> if none was found.</returns>
public static IConventionForeignKey FindSharedTableRootForeignKey(
[NotNull] this IConventionForeignKey foreignKey,
[NotNull] string tableName,
[CanBeNull] string schema,
[NotNull] string principalTableName,
[CanBeNull] string principalSchema)
=> (IConventionForeignKey)((IForeignKey)foreignKey).FindSharedTableRootForeignKey(
tableName, schema, principalTableName, principalSchema);
[CanBeNull] string schema)
=> (IConventionForeignKey)((IForeignKey)foreignKey).FindSharedTableRootForeignKey(tableName, schema);
}
}
10 changes: 6 additions & 4 deletions src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,18 @@ public static string GetDefaultDatabaseName(
[NotNull] string tableName,
[CanBeNull] string schema)
{
var propertyNames = index.Properties.Select(p => p.GetColumnName(tableName, schema)).ToList();
var table = StoreObjectIdentifier.Table(tableName, schema);
var propertyNames = index.Properties.Select(p => p.GetColumnName(table)).ToList();
var rootIndex = index;

// Limit traversal to avoid getting stuck in a cycle (validation will throw for these later)
// Using a hashset is detrimental to the perf when there are no cycles
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
var linkedIndex = rootIndex.DeclaringEntityType
.FindTableRowInternalForeignKeys(tableName, schema)
.FindRowInternalForeignKeys(table)
.SelectMany(fk => fk.PrincipalEntityType.GetIndexes())
.FirstOrDefault(i => i.Properties.Select(p => p.GetColumnName(tableName, schema)).SequenceEqual(propertyNames));
.FirstOrDefault(i => i.Properties.Select(p => p.GetColumnName(table)).SequenceEqual(propertyNames));
if (linkedIndex == null)
{
break;
Expand Down Expand Up @@ -287,6 +288,7 @@ public static IIndex FindSharedTableRootIndex(
Check.NotNull(index, nameof(index));
Check.NotNull(tableName, nameof(tableName));

var table = StoreObjectIdentifier.Table(tableName, schema);
var indexName = index.GetDatabaseName(tableName, schema);
var rootIndex = index;

Expand All @@ -295,7 +297,7 @@ public static IIndex FindSharedTableRootIndex(
for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++)
{
var linkedIndex = rootIndex.DeclaringEntityType
.FindTableRowInternalForeignKeys(tableName, schema)
.FindRowInternalForeignKeys(table)
.SelectMany(fk => fk.PrincipalEntityType.GetIndexes())
.FirstOrDefault(i => i.GetDatabaseName(tableName, schema) == indexName);
if (linkedIndex == null)
Expand Down
Loading

0 comments on commit 7f0a8d1

Please sign in to comment.