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

Fix for 9723. Allow namespaces to be specified. #20355

Merged
merged 4 commits into from
Mar 26, 2020
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
13 changes: 9 additions & 4 deletions src/EFCore.Design/Design/Internal/DatabaseOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public virtual SavedModelFiles ScaffoldContext(
[CanBeNull] string dbContextClassName,
[NotNull] IEnumerable<string> schemas,
[NotNull] IEnumerable<string> tables,
[CanBeNull] string modelNamespace,
[CanBeNull] string contextNamespace,
bool useDataAnnotations,
bool overwriteFiles,
bool useDatabaseNames)
Expand All @@ -90,8 +92,11 @@ public virtual SavedModelFiles ScaffoldContext(

var scaffolder = services.GetRequiredService<IReverseEngineerScaffolder>();

var modelNamespace = GetNamespaceFromOutputPath(outputDir);
var contextNamespace = GetNamespaceFromOutputPath(outputContextDir);
var finalModelNamespace = modelNamespace ?? GetNamespaceFromOutputPath(outputDir);
var finalContextNamespace =
contextNamespace ??
modelNamespace ??
GetNamespaceFromOutputPath(outputContextDir);

var scaffoldedModel = scaffolder.ScaffoldModel(
connectionString,
Expand All @@ -101,8 +106,8 @@ public virtual SavedModelFiles ScaffoldContext(
{
UseDataAnnotations = useDataAnnotations,
RootNamespace = _rootNamespace,
ModelNamespace = modelNamespace,
ContextNamespace = contextNamespace,
ModelNamespace = finalModelNamespace,
ContextNamespace = finalContextNamespace,
Language = _language,
ContextDir = MakeDirRelative(outputDir, outputContextDir),
ContextName = dbContextClassName
Expand Down
8 changes: 6 additions & 2 deletions src/EFCore.Design/Design/Internal/MigrationsOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public MigrationsOperations(
public virtual MigrationFiles AddMigration(
[NotNull] string name,
[CanBeNull] string outputDir,
[CanBeNull] string contextType)
[CanBeNull] string contextType,
[CanBeNull] string @namespace)
{
Check.NotEmpty(name, nameof(name));

Expand All @@ -102,7 +103,10 @@ public virtual MigrationFiles AddMigration(
EnsureMigrationsAssembly(services);

var scaffolder = services.GetRequiredService<IMigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration(name, _rootNamespace, subNamespace, _language);
var migration =
string.IsNullOrEmpty(@namespace)
? scaffolder.ScaffoldMigration(name, _rootNamespace, subNamespace, _language)
: scaffolder.ScaffoldMigration(name, null, @namespace, _language);
var files = scaffolder.Save(_projectDir, migration, outputDir);

return files;
Expand Down
21 changes: 16 additions & 5 deletions src/EFCore.Design/Design/OperationExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,22 +160,25 @@ public AddMigration(
var name = (string)args["name"];
var outputDir = (string)args["outputDir"];
var contextType = (string)args["contextType"];
var @namespace = (string)args["namespace"];

Execute(() => executor.AddMigrationImpl(name, outputDir, contextType));
Execute(() => executor.AddMigrationImpl(name, outputDir, contextType, @namespace));
}
}

private IDictionary AddMigrationImpl(
[NotNull] string name,
[CanBeNull] string outputDir,
[CanBeNull] string contextType)
[CanBeNull] string contextType,
[CanBeNull] string @namespace)
{
Check.NotEmpty(name, nameof(name));

var files = MigrationsOperations.AddMigration(
name,
outputDir,
contextType);
contextType,
@namespace);

return new Hashtable
{
Expand Down Expand Up @@ -443,6 +446,8 @@ public class ScaffoldContext : OperationBase
/// <para><c>useDataAnnotations</c>--Use attributes to configure the model (where possible). If false, only the fluent API is used.</para>
/// <para><c>overwriteFiles</c>--Overwrite existing files.</para>
/// <para><c>useDatabaseNames</c>--Use table and column names directly from the database.</para>
/// <para><c>modelNamespace</c>--Specify to override the namespace of the generated entity types.</para>
/// <para><c>contextNamespace</c>--Specify to override the namespace of the generated DbContext class.</para>
/// </summary>
/// <param name="executor"> The operation executor. </param>
/// <param name="resultHandler"> The <see cref="IOperationResultHandler" />. </param>
Expand All @@ -461,14 +466,17 @@ public ScaffoldContext(
var dbContextClassName = (string)args["dbContextClassName"];
var schemaFilters = (IEnumerable<string>)args["schemaFilters"];
var tableFilters = (IEnumerable<string>)args["tableFilters"];
var modelNamespace = (string)args["modelNamespace"];
var contextNamespace = (string)args["contextNamespace"];
var useDataAnnotations = (bool)args["useDataAnnotations"];
var overwriteFiles = (bool)args["overwriteFiles"];
var useDatabaseNames = (bool)args["useDatabaseNames"];

Execute(
() => executor.ScaffoldContextImpl(
provider, connectionString, outputDir, outputDbContextDir, dbContextClassName,
schemaFilters, tableFilters, useDataAnnotations, overwriteFiles, useDatabaseNames));
schemaFilters, tableFilters, modelNamespace, contextNamespace, useDataAnnotations,
overwriteFiles, useDatabaseNames));
}
}

Expand All @@ -480,6 +488,8 @@ private IDictionary ScaffoldContextImpl(
[CanBeNull] string dbContextClassName,
[NotNull] IEnumerable<string> schemaFilters,
[NotNull] IEnumerable<string> tableFilters,
[CanBeNull] string modelNamespace,
[CanBeNull] string contextNamespace,
bool useDataAnnotations,
bool overwriteFiles,
bool useDatabaseNames)
Expand All @@ -491,7 +501,8 @@ private IDictionary ScaffoldContextImpl(

var files = DatabaseOperations.ScaffoldContext(
provider, connectionString, outputDir, outputDbContextDir, dbContextClassName,
schemaFilters, tableFilters, useDataAnnotations, overwriteFiles, useDatabaseNames);
schemaFilters, tableFilters, modelNamespace, contextNamespace, useDataAnnotations,
overwriteFiles, useDatabaseNames);

return new Hashtable { ["ContextFile"] = files.ContextFile, ["EntityTypeFiles"] = files.AdditionalFiles.ToArray() };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface IMigrationsScaffolder
/// <returns> The scaffolded migration. </returns>
ScaffoldedMigration ScaffoldMigration(
[NotNull] string migrationName,
[NotNull] string rootNamespace,
[CanBeNull] string rootNamespace,
[CanBeNull] string subNamespace = null,
[CanBeNull] string language = null);

Expand Down
40 changes: 30 additions & 10 deletions src/EFCore.Design/Migrations/Design/MigrationsScaffolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,16 +53,22 @@ public MigrationsScaffolder([NotNull] MigrationsScaffolderDependencies dependenc
/// <returns> The scaffolded migration. </returns>
public virtual ScaffoldedMigration ScaffoldMigration(
[NotNull] string migrationName,
[NotNull] string rootNamespace,
[CanBeNull] string rootNamespace,
[CanBeNull] string subNamespace)
=> ScaffoldMigration(migrationName, rootNamespace, subNamespace, language: null);

/// <summary>
/// Scaffolds a new migration.
/// </summary>
/// <param name="migrationName"> The migration's name. </param>
/// <param name="rootNamespace"> The project's root namespace. </param>
/// <param name="subNamespace"> The migration's sub-namespace. </param>
/// <param name="rootNamespace">
/// The project's root namespace, <c>null</c> to indicate no automatic
/// namespace generation, just use sub-namespace as is.
/// </param>
/// <param name="subNamespace">
/// The migration's sub-namespace. Note: the root-namespace and
/// the sub-namespace should not both be empty.
/// </param>
/// <param name="language"> The project's language. </param>
/// <returns> The scaffolded migration. </returns>
public virtual ScaffoldedMigration ScaffoldMigration(
Expand All @@ -72,23 +78,30 @@ public virtual ScaffoldedMigration ScaffoldMigration(
string language = null)
{
Check.NotEmpty(migrationName, nameof(migrationName));
Check.NotEmpty(rootNamespace, nameof(rootNamespace));

if (Dependencies.MigrationsAssembly.FindMigrationId(migrationName) != null)
{
throw new OperationException(DesignStrings.DuplicateMigrationName(migrationName));
}

var overrideNamespace = rootNamespace == null;
var subNamespaceDefaulted = false;
if (string.IsNullOrEmpty(subNamespace))
if (string.IsNullOrEmpty(subNamespace) && !overrideNamespace)
{
subNamespaceDefaulted = true;
subNamespace = "Migrations";
}

var lastMigration = Dependencies.MigrationsAssembly.Migrations.LastOrDefault();

var migrationNamespace = rootNamespace + "." + subNamespace;
var migrationNamespace =
(!string.IsNullOrEmpty(rootNamespace)
&& !string.IsNullOrEmpty(subNamespace))
? rootNamespace + "." + subNamespace
: !string.IsNullOrEmpty(rootNamespace)
? rootNamespace
: subNamespace;

if (subNamespaceDefaulted)
{
migrationNamespace = GetNamespace(lastMigration.Value?.AsType(), migrationNamespace);
Expand All @@ -105,9 +118,14 @@ public virtual ScaffoldedMigration ScaffoldMigration(
{
if (subNamespaceDefaulted)
{
var builder = new StringBuilder()
.Append(rootNamespace)
.Append(".Migrations.");
var builder = new StringBuilder();
if (!string.IsNullOrEmpty(rootNamespace))
{
builder.Append(rootNamespace);
builder.Append(".");
}

builder.Append("Migrations.");

if (sanitizedContextName.EndsWith("Context", StringComparison.Ordinal))
{
Expand Down Expand Up @@ -136,7 +154,9 @@ public virtual ScaffoldedMigration ScaffoldMigration(
? Dependencies.MigrationsModelDiffer.GetDifferences(Dependencies.Model.GetRelationalModel(), lastModel)
: new List<MigrationOperation>();
var migrationId = Dependencies.MigrationsIdGenerator.GenerateId(migrationName);
var modelSnapshotNamespace = GetNamespace(modelSnapshot?.GetType(), migrationNamespace);
var modelSnapshotNamespace = overrideNamespace
? migrationNamespace
: GetNamespace(modelSnapshot?.GetType(), migrationNamespace);

var modelSnapshotName = sanitizedContextName + "ModelSnapshot";
if (modelSnapshot != null)
Expand Down
16 changes: 14 additions & 2 deletions src/EFCore.Tools/tools/EntityFrameworkCore.PS2.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ $versionErrorMessage = 'The Entity Framework Core Package Manager Console Tools
.PARAMETER StartupProject
The startup project to use. Defaults to the solution's startup project.

.PARAMETER Namespace
Specify to override the namespace for the migration.

.LINK
Remove-Migration
Update-Database
Expand All @@ -35,7 +38,8 @@ function Add-Migration(
$OutputDir,
$Context,
$Project,
$StartupProject)
$StartupProject,
$Namespace)
{
WarnIfEF6 'Add-Migration'
throw $UpdatePowerShell
Expand Down Expand Up @@ -177,6 +181,12 @@ function Remove-Migration(
.PARAMETER StartupProject
The startup project to use. Defaults to the solution's startup project.

.PARAMETER Namespace
Specify to override the namespace for the generated entity types.

.PARAMETER ContextNamespace
Specify to override the namespace for the DbContext class.

.LINK
about_EntityFrameworkCore
#>
Expand All @@ -192,7 +202,9 @@ function Scaffold-DbContext(
[switch] $UseDatabaseNames,
[switch] $Force,
$Project,
$StartupProject)
$StartupProject,
$Namespace,
$ContextNamespace)
{
throw $UpdatePowerShell
}
Expand Down
31 changes: 29 additions & 2 deletions src/EFCore.Tools/tools/EntityFrameworkCore.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ Register-TabExpansion Add-Migration @{
.PARAMETER StartupProject
The startup project to use. Defaults to the solution's startup project.

.PARAMETER Namespace
Specify to override the namespace for the migration.

.LINK
Remove-Migration
Update-Database
Expand All @@ -47,7 +50,8 @@ function Add-Migration
[string] $OutputDir,
[string] $Context,
[string] $Project,
[string] $StartupProject)
[string] $StartupProject,
[string] $Namespace)

WarnIfEF6 'Add-Migration'

Expand All @@ -61,6 +65,11 @@ function Add-Migration
$params += '--output-dir', $OutputDir
}

if ($Namespace)
{
$params += '--namespace', $Namespace
}

$params += GetParams $Context

# NB: -join is here to support ConvertFrom-Json on PowerShell 3.0
Expand Down Expand Up @@ -305,6 +314,12 @@ Register-TabExpansion Scaffold-DbContext @{
.PARAMETER StartupProject
The startup project to use. Defaults to the solution's startup project.

.PARAMETER Namespace
Specify to override the namespace for the generated entity types.

.PARAMETER ContextNamespace
Specify to override the namespace for the DbContext class.

.LINK
about_EntityFrameworkCore
#>
Expand All @@ -325,7 +340,9 @@ function Scaffold-DbContext
[switch] $UseDatabaseNames,
[switch] $Force,
[string] $Project,
[string] $StartupProject)
[string] $StartupProject,
[string] $Namespace,
[string] $ContextNamespace)

$dteProject = GetProject $Project
$dteStartupProject = GetStartupProject $StartupProject $dteProject
Expand All @@ -347,6 +364,16 @@ function Scaffold-DbContext
$params += '--context', $Context
}

if ($Namespace)
{
$params += '--namespace', $Namespace
}

if ($ContextNamespace)
{
$params += '--context-namespace', $ContextNamespace
}

$params += $Schemas | %{ '--schema', $_ }
$params += $Tables | %{ '--table', $_ }

Expand Down
18 changes: 18 additions & 0 deletions src/dotnet-ef/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/dotnet-ef/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,13 @@
<data name="DbContextConnectionDescription" xml:space="preserve">
<value>The connection string to the database. Defaults to the one specified in AddDbContext or OnConfiguring.</value>
</data>
<data name="NamespaceDescription" xml:space="preserve">
<value>Specify to override the namespace for the generated entity types.</value>
</data>
<data name="ContextNamespaceDescription" xml:space="preserve">
<value>Specify to override the namespace for the DbContext class.</value>
</data>
<data name="MigrationsNamespaceDescription" xml:space="preserve">
<value>Specify to override the namespace for the migration.</value>
</data>
</root>
Loading