From 5a759d5f61d2e28fc651f7059ce8b8bc0fe7ee3a Mon Sep 17 00:00:00 2001 From: Daniele Maltese <17677294+ilmalte@users.noreply.github.com> Date: Wed, 18 May 2022 20:50:29 +0200 Subject: [PATCH] Capitalize database name (#27966) When scaffolding from a database the database name is modified in order to respect .NET conventions #Fixes 27886 --- .../Internal/CandidateNamingService.cs | 8 ++++- .../Internal/ICandidateNamingService.cs | 9 ++++++ .../RelationalScaffoldingModelFactory.cs | 5 +++- .../RelationalScaffoldingModelFactoryTest.cs | 29 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs b/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs index 5cb31203222..5570a0aaa02 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs @@ -65,7 +65,13 @@ public virtual string GetPrincipalEndCandidateNavigationPropertyName( : foreignKey.DeclaringEntityType.ShortName(); } - private static string GenerateCandidateIdentifier(string originalIdentifier) + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual string GenerateCandidateIdentifier(string originalIdentifier) { var candidateStringBuilder = new StringBuilder(); var previousLetterCharInWordIsLowerCase = false; diff --git a/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs b/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs index 45bf5e1c8c0..16a22718321 100644 --- a/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs +++ b/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs @@ -29,6 +29,15 @@ public interface ICandidateNamingService /// string GenerateCandidateIdentifier(DatabaseColumn originalColumn); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + string GenerateCandidateIdentifier(string databaseName); + + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs index 839dfbcf7c4..2ce3b6ab19e 100644 --- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs +++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs @@ -166,7 +166,10 @@ protected virtual ModelBuilder VisitDatabaseModel(ModelBuilder modelBuilder, Dat if (!string.IsNullOrEmpty(databaseModel.DatabaseName)) { - modelBuilder.Model.SetDatabaseName(databaseModel.DatabaseName); + modelBuilder.Model.SetDatabaseName( + !_options.UseDatabaseNames && !string.IsNullOrEmpty(databaseModel.DatabaseName) + ? _candidateNamingService.GenerateCandidateIdentifier(databaseModel.DatabaseName) + : databaseModel.DatabaseName); } if (!string.IsNullOrEmpty(databaseModel.Collation)) diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs index 6cf32d7cebf..2741df760e7 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs @@ -50,6 +50,14 @@ public RelationalScaffoldingModelFactoryTest() _reporter.Clear(); } + [ConditionalFact] + public void Capitalize_DatabaseName() + { + var database = new DatabaseModel { DatabaseName = "northwind" }; + var model = _factory.Create(database, new ModelReverseEngineerOptions { UseDatabaseNames = false }); + Assert.Equal("Northwind", model.GetDatabaseName()); + } + [ConditionalFact] public void Creates_entity_types() { @@ -131,6 +139,27 @@ public void Creates_entity_types_case_insensitive() Assert.Equal(2, model.GetEntityTypes().Select(et => et.Name).Distinct(StringComparer.OrdinalIgnoreCase).Count()); } + [ConditionalTheory] + [InlineData("PascalCase")] + [InlineData("camelCase")] + [InlineData("snake-case")] + [InlineData("MixedCASE")] + [InlineData("separated_by_underscores")] + [InlineData("PascalCase_withUnderscore")] + [InlineData("ALL_CAPS")] + [InlineData("numbers0Dont1Affect23Upper45Case678To9LowerCase10Boundary999")] + [InlineData("We1!*~&%rdCh@r^act()0rs")] + public void Get_DatabaseName(string expectedValue) + { + var options = new ModelReverseEngineerOptions { UseDatabaseNames = true }; + + var database = new DatabaseModel { DatabaseName = expectedValue }; + var model = _factory.Create(database, options); + Assert.Equal(expectedValue, model.GetDatabaseName()); + + } + + [ConditionalFact] public void Loads_column_types() {