diff --git a/src/EFCore.Design/Properties/DesignStrings.Designer.cs b/src/EFCore.Design/Properties/DesignStrings.Designer.cs index 25ce7212218..42479c8a5b3 100644 --- a/src/EFCore.Design/Properties/DesignStrings.Designer.cs +++ b/src/EFCore.Design/Properties/DesignStrings.Designer.cs @@ -230,6 +230,14 @@ public static string ForeignKeyScaffoldErrorPropertyNotFound([CanBeNull] object GetString("ForeignKeyScaffoldErrorPropertyNotFound", nameof(foreignKeyName), nameof(columnNames)), foreignKeyName, columnNames); + /// + /// Could not scaffold the foreign key '{foreignKeyName}'. Foreign key '{existingForeignKey}' is defined on same columns targeting same key on principal table. + /// + public static string ForeignKeyWithSameFacetsExists([CanBeNull] object foreignKeyName, [CanBeNull] object existingForeignKey) + => string.Format( + GetString("ForeignKeyWithSameFacetsExists", nameof(foreignKeyName), nameof(existingForeignKey)), + foreignKeyName, existingForeignKey); + /// /// The namespace '{migrationsNamespace}' contains migrations for a different DbContext. This can result in conflicting migration names. It's recommend to put migrations for different DbContext classes into different namespaces. /// @@ -383,7 +391,7 @@ public static string NonNullableBoooleanColumnHasDefaultConstraint([CanBeNull] o columnName); /// - /// The provider '{provider}' is not a Relational provider and therefore cannot be use with Migrations. + /// The provider '{provider}' is not a Relational provider and therefore cannot be used with Migrations. /// public static string NonRelationalProvider([CanBeNull] object provider) => string.Format( @@ -455,7 +463,7 @@ public static string ProviderReturnedNullModel([CanBeNull] object providerTypeNa providerTypeName); /// - /// No files were generated in directory '{outputDirectoryName}'. The following file(s) already exist and must be made writeable to continue: {readOnlyFiles}. + /// No files were generated in directory '{outputDirectoryName}'. The following file(s) already exist(s) and must be made writeable to continue: {readOnlyFiles}. /// public static string ReadOnlyFiles([CanBeNull] object outputDirectoryName, [CanBeNull] object readOnlyFiles) => string.Format( diff --git a/src/EFCore.Design/Properties/DesignStrings.resx b/src/EFCore.Design/Properties/DesignStrings.resx index 3c16f317ceb..5eeca7bf272 100644 --- a/src/EFCore.Design/Properties/DesignStrings.resx +++ b/src/EFCore.Design/Properties/DesignStrings.resx @@ -201,6 +201,9 @@ Could not scaffold the foreign key '{foreignKeyName}'. The following columns in the foreign key could not be scaffolded: {columnNames}. + + Could not scaffold the foreign key '{foreignKeyName}'. Foreign key '{existingForeignKey}' is defined on same columns targeting same key on principal table. + The namespace '{migrationsNamespace}' contains migrations for a different DbContext. This can result in conflicting migration names. It's recommend to put migrations for different DbContext classes into different namespaces. @@ -369,4 +372,4 @@ Change your target project to the migrations project by using the Package Manage Writing model snapshot to '{file}'. - + \ No newline at end of file diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs index be28f80b28f..a651dfa2a5b 100644 --- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs +++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs @@ -748,7 +748,6 @@ protected virtual IMutableForeignKey VisitForeignKey([NotNull] ModelBuilder mode } var dependentEntityType = modelBuilder.Model.FindEntityType(GetEntityTypeName(foreignKey.Table)); - if (dependentEntityType == null) { return null; @@ -824,9 +823,7 @@ protected virtual IMutableForeignKey VisitForeignKey([NotNull] ModelBuilder mode nullablePrincipalProperties.Select(tuple => tuple.column.DisplayName()).ToList() .Aggregate((a, b) => a + "," + b))); - nullablePrincipalProperties - .ToList() - .ForEach(tuple => tuple.property.IsNullable = false); + nullablePrincipalProperties.ForEach(tuple => tuple.property.IsNullable = false); } principalKey = principalEntityType.AddKey(principalProperties); @@ -845,6 +842,15 @@ protected virtual IMutableForeignKey VisitForeignKey([NotNull] ModelBuilder mode } } + var existingForeignKey = dependentEntityType.FindForeignKey(dependentProperties, principalKey, principalEntityType); + if (existingForeignKey is not null) + { + _reporter.WriteWarning( + DesignStrings.ForeignKeyWithSameFacetsExists(foreignKey.DisplayName(), existingForeignKey.GetConstraintName())); + + return null; + } + var newForeignKey = dependentEntityType.AddForeignKey( dependentProperties, principalKey, principalEntityType); diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs index d9ef9744d46..6ebfb7126f1 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs @@ -1145,6 +1145,64 @@ public void It_logs_warning_for_bad_foreign_key() childrenTable.ForeignKeys.ElementAt(0).DisplayName(), "NotPkId", "Parent"))); } + [ConditionalFact] + public void It_logs_warning_for_duplicate_foreign_key() + { + var parentTable = new DatabaseTable + { + Database = Database, + Name = "Parent", + Columns = + { + IdColumn + }, + PrimaryKey = IdPrimaryKey + }; + var childrenTable = new DatabaseTable + { + Database = Database, + Name = "Children", + Columns = + { + IdColumn, + new DatabaseColumn + { + Table = Table, + Name = "ParentId", + StoreType = "int" + } + }, + PrimaryKey = IdPrimaryKey + }; + childrenTable.ForeignKeys.Add( + new DatabaseForeignKey + { + Table = childrenTable, + Name = "FK_Foo", + Columns = { childrenTable.Columns.ElementAt(1) }, + PrincipalTable = parentTable, + PrincipalColumns = { parentTable.Columns.ElementAt(0) } + }); + childrenTable.ForeignKeys.Add( + new DatabaseForeignKey + { + Table = childrenTable, + Name = "FK_Another_Foo", + Columns = { childrenTable.Columns.ElementAt(1) }, + PrincipalTable = parentTable, + PrincipalColumns = { parentTable.Columns.ElementAt(0) } + }); + + _factory.Create( + new DatabaseModel { Tables = { parentTable, childrenTable } }, + new ModelReverseEngineerOptions()); + + Assert.Single( + _reporter.Messages, t => t.Contains( + "warn: " + + DesignStrings.ForeignKeyWithSameFacetsExists(childrenTable.ForeignKeys.ElementAt(1).DisplayName(), "FK_Foo"))); + } + [ConditionalFact] public void Unique_nullable_index_unused_by_foreign_key() {