Skip to content

Commit

Permalink
RevEng: Use pluralizer with database names too
Browse files Browse the repository at this point in the history
  • Loading branch information
bricelam committed Aug 20, 2020
1 parent e567b4d commit def7b3b
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 24 deletions.
20 changes: 17 additions & 3 deletions src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,23 @@ private static string FindCommonPrefix(string firstName, IEnumerable<string> pro

private static string StripId(string commonPrefix)
{
return commonPrefix.Length > 2
&& commonPrefix.EndsWith("id", StringComparison.OrdinalIgnoreCase)
? commonPrefix[..^2]
if (commonPrefix.Length < 3
|| !commonPrefix.EndsWith("id", StringComparison.OrdinalIgnoreCase))
{
return commonPrefix;
}

int i;
for (i = commonPrefix.Length - 3; i >= 0; i--)
{
if (char.IsLetterOrDigit(commonPrefix[i]))
{
break;
}
}

return i != 0
? commonPrefix.Substring(0, i + 1)
: commonPrefix;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,15 @@ public virtual IModel Create(DatabaseModel databaseModel, ModelReverseEngineerOp
? (Func<DatabaseTable, string>)(t => t.Name)
: t => _candidateNamingService.GenerateCandidateIdentifier(t),
_cSharpUtilities,
options.UseDatabaseNames || options.NoPluralize
options.NoPluralize
? (Func<string, string>)null
: _pluralizer.Singularize);
_dbSetNamer = new CSharpUniqueNamer<DatabaseTable>(
options.UseDatabaseNames
? (Func<DatabaseTable, string>)(t => t.Name)
: t => _candidateNamingService.GenerateCandidateIdentifier(t),
_cSharpUtilities,
options.UseDatabaseNames || options.NoPluralize
options.NoPluralize
? (Func<string, string>)null
: _pluralizer.Pluralize);
_columnNamers = new Dictionary<DatabaseTable, CSharpUniqueNamer<DatabaseColumn>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ public void Use_database_names_for_columns()
}
};

var entityType = _factory.Create(info, new ModelReverseEngineerOptions { UseDatabaseNames = true }).FindEntityType("NaturalProducts");
var entityType = _factory
.Create(info, new ModelReverseEngineerOptions { UseDatabaseNames = true, NoPluralize = true })
.FindEntityType("NaturalProducts");

Assert.Collection(
entityType.GetProperties(),
Expand Down Expand Up @@ -1230,24 +1232,6 @@ public void Pluralization_of_entity_and_DbSet()
Assert.Equal("Posts", entity.GetDbSetName());
}
);

model = factory.Create(info, new ModelReverseEngineerOptions { UseDatabaseNames = true });

Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast<EntityType>(),
entity =>
{
Assert.Equal("Blog", entity.GetTableName());
Assert.Equal("Blog", entity.Name);
Assert.Equal("Blog", entity.GetDbSetName());
},
entity =>
{
Assert.Equal("Posts", entity.GetTableName());
Assert.Equal("Posts", entity.Name);
Assert.Equal("Posts", entity.GetDbSetName());
}
);
}

[ConditionalFact]
Expand Down Expand Up @@ -1668,5 +1652,131 @@ public void Column_and_table_comments()
var column = model.FindEntityType("Table").GetProperty("Column");
Assert.Equal("An int column", column.GetComment());
}

[ConditionalTheory]
[InlineData(false, false, false)]
[InlineData(false, false, true)]
[InlineData(false, true, false)]
[InlineData(false, true, true)]
[InlineData(true, false, false)]
[InlineData(true, false, true)]
[InlineData(true, true, false)]
[InlineData(true, true, true)]
public void UseDatabaseNames_and_NoPluralize_work_together(
bool useDatabaseNames,
bool noPluralize,
bool pluralTables)
{
var userTableName = pluralTables ? "users" : "user";
var postTableName = pluralTables ? "posts" : "post";
var databaseModel = new DatabaseModel
{
Tables =
{
new DatabaseTable
{
Name = userTableName,
Columns =
{
new DatabaseColumn
{
Name = "id",
StoreType = "int"
}
},
PrimaryKey = new DatabasePrimaryKey
{
Columns = { new DatabaseColumnRef("id") }
}
},
new DatabaseTable
{
Name = postTableName,
Columns =
{
new DatabaseColumn
{
Name = "id",
StoreType = "int"
},
new DatabaseColumn
{
Name = "author_id",
StoreType = "int"
}
},
PrimaryKey = new DatabasePrimaryKey
{
Columns = { new DatabaseColumnRef("id") }
},
ForeignKeys =
{
new DatabaseForeignKey
{
PrincipalTable = new DatabaseTableRef(userTableName),
Columns = { new DatabaseColumnRef("author_id") },
PrincipalColumns = { new DatabaseColumnRef("id") }
}
}
}
}
};

var model = _factory.Create(
databaseModel,
new ModelReverseEngineerOptions { UseDatabaseNames = useDatabaseNames, NoPluralize = noPluralize });

var user = Assert.Single(model.GetEntityTypes().Where(e => e.GetTableName() == userTableName));
var id = Assert.Single(user.GetProperties().Where(p => p.GetColumnName() == "id"));
var foreignKey = Assert.Single(user.GetReferencingForeignKeys());
if (useDatabaseNames && noPluralize)
{
Assert.Equal(userTableName, user.Name);
Assert.Equal(userTableName, user[ScaffoldingAnnotationNames.DbSetName]);
Assert.Equal("id", id.Name);
Assert.Equal(postTableName, foreignKey.PrincipalToDependent.Name);
Assert.Equal("author_id", Assert.Single(foreignKey.Properties).Name);
Assert.Equal("author", foreignKey.DependentToPrincipal.Name);
}
else if (useDatabaseNames)
{
Assert.Equal("user", user.Name);
Assert.Equal("users", user[ScaffoldingAnnotationNames.DbSetName]);
Assert.Equal("id", id.Name);
Assert.Equal("posts", foreignKey.PrincipalToDependent.Name);
Assert.Equal("author_id", Assert.Single(foreignKey.Properties).Name);
Assert.Equal("author", foreignKey.DependentToPrincipal.Name);
}
else if (noPluralize)
{
if (pluralTables)
{
Assert.Equal("Users", user.Name);
Assert.Equal("Users", user[ScaffoldingAnnotationNames.DbSetName]);
Assert.Equal("Id", id.Name);
Assert.Equal("Posts", foreignKey.PrincipalToDependent.Name);
Assert.Equal("AuthorId", Assert.Single(foreignKey.Properties).Name);
Assert.Equal("Author", foreignKey.DependentToPrincipal.Name);
}
else
{
Assert.Equal("User", user.Name);
Assert.Equal("User", user[ScaffoldingAnnotationNames.DbSetName]);
Assert.Equal("Id", id.Name);
Assert.Equal("Post", foreignKey.PrincipalToDependent.Name);
Assert.Equal("AuthorId", Assert.Single(foreignKey.Properties).Name);
Assert.Equal("Author", foreignKey.DependentToPrincipal.Name);
}
}
else
{
Assert.Equal("User", user.Name);
Assert.Equal("Users", user[ScaffoldingAnnotationNames.DbSetName]);
Assert.Equal("Id", id.Name);
Assert.Equal("Posts", foreignKey.PrincipalToDependent.Name);
Assert.Equal("AuthorId", Assert.Single(foreignKey.Properties).Name);
Assert.Equal("Author", foreignKey.DependentToPrincipal.Name);
}
}
}
}
59 changes: 59 additions & 0 deletions test/EFCore.Design.Tests/TestUtilities/DatabaseColumnRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;

namespace Microsoft.EntityFrameworkCore.TestUtilities
{
class DatabaseColumnRef : DatabaseColumn
{
public DatabaseColumnRef(string name)
{
Name = name;
}

public override DatabaseTable Table
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override bool IsNullable
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override string StoreType
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override string DefaultValueSql
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override string ComputedColumnSql
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override string Comment
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override ValueGenerated? ValueGenerated
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
}
}
48 changes: 48 additions & 0 deletions test/EFCore.Design.Tests/TestUtilities/DatabaseTableRef.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;

namespace Microsoft.EntityFrameworkCore.TestUtilities
{
class DatabaseTableRef : DatabaseTable
{
public DatabaseTableRef(string name, string schema = null)
{
Name = name;
Schema = schema;
}

public override DatabaseModel Database
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override string Comment
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override DatabasePrimaryKey PrimaryKey
{
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}

public override IList<DatabaseColumn> Columns
=> throw new NotImplementedException();

public override IList<DatabaseUniqueConstraint> UniqueConstraints
=> throw new NotImplementedException();

public override IList<DatabaseIndex> Indexes
=> throw new NotImplementedException();

public override IList<DatabaseForeignKey> ForeignKeys
=> throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
Expand Down Expand Up @@ -64,6 +65,12 @@ public override IModel Create(DatabaseModel databaseModel, ModelReverseEngineerO
foreignKey.Table = table;
FixupColumns(table, foreignKey.Columns);

if (foreignKey.PrincipalTable is DatabaseTableRef tableRef)
{
foreignKey.PrincipalTable = databaseModel.Tables
.First(t => t.Name == tableRef.Name && t.Schema == tableRef.Schema);
}

FixupColumns(foreignKey.PrincipalTable, foreignKey.PrincipalColumns);
}
}
Expand All @@ -75,6 +82,11 @@ private static void FixupColumns(DatabaseTable table, IList<DatabaseColumn> colu
{
for (var i = 0; i < columns.Count; i++)
{
if (columns[i] is DatabaseColumnRef columnRef)
{
columns[i] = table.Columns.First(c => c.Name == columnRef.Name);
}

columns[i].Table = table;
}
}
Expand Down

0 comments on commit def7b3b

Please sign in to comment.