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

Uniquify FK and Key constraint names across tables #28753

Merged
merged 1 commit into from
Aug 17, 2022
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
138 changes: 84 additions & 54 deletions src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,24 @@ public virtual void ProcessModelFinalizing(
TryUniquifyTableNames(modelBuilder.Metadata, tables, maxLength);

var columns = new Dictionary<string, IConventionProperty>();
var keys = new Dictionary<string, IConventionKey>();
var foreignKeys = new Dictionary<string, IConventionForeignKey>();
var indexes = new Dictionary<string, IConventionIndex>();
var checkConstraints = new Dictionary<(string, string?), IConventionCheckConstraint>();
var triggers = new Dictionary<string, IConventionTrigger>();
var keys = new Dictionary<string, (IConventionKey, StoreObjectIdentifier)>();
var foreignKeys = new Dictionary<string, (IConventionForeignKey, StoreObjectIdentifier)>();
var indexes = new Dictionary<string, (IConventionIndex, StoreObjectIdentifier)>();
var checkConstraints = new Dictionary<(string, string?), (IConventionCheckConstraint, StoreObjectIdentifier)>();
var triggers = new Dictionary<string, (IConventionTrigger, StoreObjectIdentifier)>();
foreach (var ((tableName, schema), conventionEntityTypes) in tables)
{
columns.Clear();
keys.Clear();
foreignKeys.Clear();

if (!KeysUniqueAcrossTables)
{
keys.Clear();
}

if (!ForeignKeysUniqueAcrossTables)
{
foreignKeys.Clear();
}

if (!IndexesUniqueAcrossTables)
{
Expand Down Expand Up @@ -86,6 +94,18 @@ public virtual void ProcessModelFinalizing(
}
}

/// <summary>
/// Gets a value indicating whether key names should be unique across tables.
/// </summary>
protected virtual bool KeysUniqueAcrossTables
=> false;

/// <summary>
/// Gets a value indicating whether foreign key names should be unique across tables.
/// </summary>
protected virtual bool ForeignKeysUniqueAcrossTables
=> false;

/// <summary>
/// Gets a value indicating whether index names should be unique across tables.
/// </summary>
Expand Down Expand Up @@ -297,7 +317,7 @@ private static void TryUniquifyColumnNames(

private void TryUniquifyKeyNames(
IConventionEntityType entityType,
Dictionary<string, IConventionKey> keys,
Dictionary<string, (IConventionKey, StoreObjectIdentifier)> keys,
in StoreObjectIdentifier storeObject,
int maxLength)
{
Expand All @@ -309,31 +329,33 @@ private void TryUniquifyKeyNames(
continue;
}

if (!keys.TryGetValue(keyName, out var otherKey))
if (!keys.TryGetValue(keyName, out var otherKeyPair))
{
keys[keyName] = key;
keys[keyName] = (key, storeObject);
continue;
}

if ((key.IsPrimaryKey()
&& otherKey.IsPrimaryKey())
|| AreCompatible(key, otherKey, storeObject))
var (otherKey, otherStoreObject) = otherKeyPair;
if (storeObject == otherStoreObject
&& ((key.IsPrimaryKey()
&& otherKey.IsPrimaryKey())
|| AreCompatible(key, otherKey, storeObject)))
{
continue;
}

var newKeyName = TryUniquify(key, keyName, keys, maxLength);
if (newKeyName != null)
{
keys[newKeyName] = key;
keys[newKeyName] = (key, storeObject);
continue;
}

var newOtherKeyName = TryUniquify(otherKey, keyName, keys, maxLength);
if (newOtherKeyName != null)
{
keys[keyName] = key;
keys[newOtherKeyName] = otherKey;
keys[keyName] = (key, storeObject);
keys[newOtherKeyName] = otherKeyPair;
}
}
}
Expand All @@ -351,10 +373,10 @@ protected virtual bool AreCompatible(
in StoreObjectIdentifier storeObject)
=> key.AreCompatible(duplicateKey, storeObject, shouldThrow: false);

private static string? TryUniquify<T>(
private static string? TryUniquify(
IConventionKey key,
string keyName,
Dictionary<string, T> keys,
Dictionary<string, (IConventionKey, StoreObjectIdentifier)> keys,
int maxLength)
{
if (key.Builder.CanSetName(null))
Expand All @@ -369,7 +391,7 @@ protected virtual bool AreCompatible(

private void TryUniquifyIndexNames(
IConventionEntityType entityType,
Dictionary<string, IConventionIndex> indexes,
Dictionary<string, (IConventionIndex, StoreObjectIdentifier)> indexes,
in StoreObjectIdentifier storeObject,
int maxLength)
{
Expand All @@ -381,29 +403,31 @@ private void TryUniquifyIndexNames(
continue;
}

if (!indexes.TryGetValue(indexName, out var otherIndex))
if (!indexes.TryGetValue(indexName, out var otherIndexPair))
{
indexes[indexName] = index;
indexes[indexName] = (index, storeObject);
continue;
}

if (AreCompatible(index, otherIndex, storeObject))
var (otherIndex, otherStoreObject) = otherIndexPair;
if (storeObject == otherStoreObject
&& AreCompatible(index, otherIndex, storeObject))
{
continue;
}

var newIndexName = TryUniquify(index, indexName, indexes, maxLength);
if (newIndexName != null)
{
indexes[newIndexName] = index;
indexes[newIndexName] = (index, storeObject);
continue;
}

var newOtherIndexName = TryUniquify(otherIndex, indexName, indexes, maxLength);
if (newOtherIndexName != null)
{
indexes[indexName] = index;
indexes[newOtherIndexName] = otherIndex;
indexes[indexName] = (index, storeObject);
indexes[newOtherIndexName] = otherIndexPair;
}
}
}
Expand All @@ -421,10 +445,10 @@ protected virtual bool AreCompatible(
in StoreObjectIdentifier storeObject)
=> index.AreCompatible(duplicateIndex, storeObject, shouldThrow: false);

private static string? TryUniquify<T>(
private static string? TryUniquify(
IConventionIndex index,
string indexName,
Dictionary<string, T> indexes,
Dictionary<string, (IConventionIndex, StoreObjectIdentifier)> indexes,
int maxLength)
{
if (index.Builder.CanSetDatabaseName(null))
Expand All @@ -439,7 +463,7 @@ protected virtual bool AreCompatible(

private void TryUniquifyForeignKeyNames(
IConventionEntityType entityType,
Dictionary<string, IConventionForeignKey> foreignKeys,
Dictionary<string, (IConventionForeignKey, StoreObjectIdentifier)> foreignKeys,
in StoreObjectIdentifier storeObject,
int maxLength)
{
Expand All @@ -466,21 +490,23 @@ private void TryUniquifyForeignKeyNames(
continue;
}

if (!foreignKeys.TryGetValue(foreignKeyName, out var otherForeignKey))
if (!foreignKeys.TryGetValue(foreignKeyName, out var otherForeignKeyPair))
{
foreignKeys[foreignKeyName] = foreignKey;
foreignKeys[foreignKeyName] = (foreignKey, storeObject);
continue;
}

if (AreCompatible(foreignKey, otherForeignKey, storeObject))
var (otherForeignKey, otherStoreObject) = otherForeignKeyPair;
if (storeObject == otherStoreObject
&& AreCompatible(foreignKey, otherForeignKey, storeObject))
{
continue;
}

var newForeignKeyName = TryUniquify(foreignKey, foreignKeyName, foreignKeys, maxLength);
if (newForeignKeyName != null)
{
foreignKeys[newForeignKeyName] = foreignKey;
foreignKeys[newForeignKeyName] = (foreignKey, storeObject);
continue;
}

Expand All @@ -493,8 +519,8 @@ private void TryUniquifyForeignKeyNames(
var newOtherForeignKeyName = TryUniquify(otherForeignKey, foreignKeyName, foreignKeys, maxLength);
if (newOtherForeignKeyName != null)
{
foreignKeys[foreignKeyName] = foreignKey;
foreignKeys[newOtherForeignKeyName] = otherForeignKey;
foreignKeys[foreignKeyName] = (foreignKey, storeObject);
foreignKeys[newOtherForeignKeyName] = otherForeignKeyPair;
}
}
}
Expand All @@ -512,10 +538,10 @@ protected virtual bool AreCompatible(
in StoreObjectIdentifier storeObject)
=> foreignKey.AreCompatible(duplicateForeignKey, storeObject, shouldThrow: false);

private static string? TryUniquify<T>(
private static string? TryUniquify(
IConventionForeignKey foreignKey,
string foreignKeyName,
Dictionary<string, T> foreignKeys,
Dictionary<string, (IConventionForeignKey, StoreObjectIdentifier)> foreignKeys,
int maxLength)
{
if (foreignKey.Builder.CanSetConstraintName(null))
Expand All @@ -530,7 +556,7 @@ protected virtual bool AreCompatible(

private void TryUniquifyCheckConstraintNames(
IConventionEntityType entityType,
Dictionary<(string, string?), IConventionCheckConstraint> checkConstraints,
Dictionary<(string, string?), (IConventionCheckConstraint, StoreObjectIdentifier)> checkConstraints,
in StoreObjectIdentifier storeObject,
int maxLength)
{
Expand All @@ -542,29 +568,31 @@ private void TryUniquifyCheckConstraintNames(
continue;
}

if (!checkConstraints.TryGetValue((constraintName, storeObject.Schema), out var otherCheckConstraint))
if (!checkConstraints.TryGetValue((constraintName, storeObject.Schema), out var otherCheckConstraintPair))
{
checkConstraints[(constraintName, storeObject.Schema)] = checkConstraint;
checkConstraints[(constraintName, storeObject.Schema)] = (checkConstraint, storeObject);
continue;
}

if (AreCompatible(checkConstraint, otherCheckConstraint, storeObject))
var (otherCheckConstraint, otherStoreObject) = otherCheckConstraintPair;
if (storeObject == otherStoreObject
&& AreCompatible(checkConstraint, otherCheckConstraint, storeObject))
{
continue;
}

var newConstraintName = TryUniquify(checkConstraint, constraintName, storeObject.Schema, checkConstraints, maxLength);
if (newConstraintName != null)
{
checkConstraints[(newConstraintName, storeObject.Schema)] = checkConstraint;
checkConstraints[(newConstraintName, storeObject.Schema)] = (checkConstraint, storeObject);
continue;
}

var newOtherConstraintName = TryUniquify(otherCheckConstraint, constraintName, storeObject.Schema, checkConstraints, maxLength);
if (newOtherConstraintName != null)
{
checkConstraints[(constraintName, storeObject.Schema)] = checkConstraint;
checkConstraints[(newOtherConstraintName, storeObject.Schema)] = otherCheckConstraint;
checkConstraints[(constraintName, storeObject.Schema)] = (checkConstraint, storeObject);
checkConstraints[(newOtherConstraintName, otherStoreObject.Schema)] = otherCheckConstraintPair;
}
}
}
Expand All @@ -582,11 +610,11 @@ protected virtual bool AreCompatible(
in StoreObjectIdentifier storeObject)
=> CheckConstraint.AreCompatible(checkConstraint, duplicateCheckConstraint, storeObject, shouldThrow: false);

private static string? TryUniquify<T>(
private static string? TryUniquify(
IConventionCheckConstraint checkConstraint,
string checkConstraintName,
string? schema,
Dictionary<(string, string?), T> checkConstraints,
Dictionary<(string, string?), (IConventionCheckConstraint, StoreObjectIdentifier)> checkConstraints,
int maxLength)
{
if (checkConstraint.Builder.CanSetName(null))
Expand All @@ -601,7 +629,7 @@ protected virtual bool AreCompatible(

private void TryUniquifyTriggerNames(
IConventionEntityType entityType,
Dictionary<string, IConventionTrigger> triggers,
Dictionary<string, (IConventionTrigger, StoreObjectIdentifier)> triggers,
in StoreObjectIdentifier storeObject,
int maxLength)
{
Expand All @@ -613,29 +641,31 @@ private void TryUniquifyTriggerNames(
continue;
}

if (!triggers.TryGetValue(triggerName, out var otherTrigger))
if (!triggers.TryGetValue(triggerName, out var otherTriggerPair))
{
triggers[triggerName] = trigger;
triggers[triggerName] = (trigger, storeObject);
continue;
}

if (AreCompatible(trigger, otherTrigger, storeObject))
var (otherTrigger, otherStoreObject) = otherTriggerPair;
if (otherStoreObject == storeObject
&& AreCompatible(trigger, otherTrigger, storeObject))
{
continue;
}

var newTriggerName = TryUniquify(trigger, triggerName, triggers, maxLength);
if (newTriggerName != null)
{
triggers[newTriggerName] = trigger;
triggers[newTriggerName] = (trigger, storeObject);
continue;
}

var newOtherTrigger = TryUniquify(otherTrigger, triggerName, triggers, maxLength);
if (newOtherTrigger != null)
{
triggers[triggerName] = trigger;
triggers[newOtherTrigger] = otherTrigger;
triggers[triggerName] = (trigger, storeObject);
triggers[newOtherTrigger] = otherTriggerPair;
}
}
}
Expand All @@ -653,10 +683,10 @@ protected virtual bool AreCompatible(
in StoreObjectIdentifier storeObject)
=> true;

private static string? TryUniquify<T>(
private static string? TryUniquify(
IConventionTrigger trigger,
string triggerName,
Dictionary<string, T> triggers,
Dictionary<string, (IConventionTrigger, StoreObjectIdentifier)> triggers,
int maxLength)
{
if (trigger.Builder.CanSetName(null))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public override ConventionSet CreateConventionSet()
conventionSet.Replace<ValueGenerationConvention>(
new SqlServerValueGenerationConvention(Dependencies, RelationalDependencies));
conventionSet.Replace<RuntimeModelConvention>(new SqlServerRuntimeModelConvention(Dependencies, RelationalDependencies));
conventionSet.Replace<SharedTableConvention>(
new SqlServerSharedTableConvention(Dependencies, RelationalDependencies));

var sqlServerTemporalConvention = new SqlServerTemporalConvention(Dependencies, RelationalDependencies);
ConventionSet.AddBefore(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ public SqlServerSharedTableConvention(
{
}

/// <inheritdoc />
protected override bool IndexesUniqueAcrossTables => false;

/// <inheritdoc />
protected override bool AreCompatible(IReadOnlyKey key, IReadOnlyKey duplicateKey, in StoreObjectIdentifier storeObject)
=> base.AreCompatible(key, duplicateKey, storeObject)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.



// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public override void Identifiers_are_generated_correctly()
"ExtraPropertyWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingC~1",
entityType2.GetProperties().ElementAt(2).GetColumnName(StoreObjectIdentifier.Table(entityType2.GetTableName())));
Assert.Equal(
"IX_LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWor~1",
"IX_LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWork~",
entityType2.GetIndexes().Single().GetDatabaseName());
}

Expand Down
Loading