diff --git a/src/EFCore.Design/Design/OperationExecutor.cs b/src/EFCore.Design/Design/OperationExecutor.cs index a7a93f2f08b..189ab3114fe 100644 --- a/src/EFCore.Design/Design/OperationExecutor.cs +++ b/src/EFCore.Design/Design/OperationExecutor.cs @@ -454,6 +454,13 @@ private IDictionary ScaffoldContextImpl( Check.NotNull(schemaFilters, nameof(schemaFilters)); Check.NotNull(tableFilters, nameof(tableFilters)); + // In package manager console, relative outputDir is relative to project directory + if (!string.IsNullOrWhiteSpace(outputDir) + && !Path.IsPathRooted(outputDir)) + { + outputDir = Path.GetFullPath(Path.Combine(_projectDir, outputDir)); + } + var files = _databaseOperations.Value.ScaffoldContext( provider, connectionString, outputDir, dbContextClassName, schemaFilters, tableFilters, useDataAnnotations, overwriteFiles, useDatabaseNames); diff --git a/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs b/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs index cb46959844d..49f862328ca 100644 --- a/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs +++ b/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs @@ -93,26 +93,14 @@ public virtual ReverseEngineerFiles Generate( _factory.GetType().ShortDisplayName())); } - projectPath = projectPath.TrimEnd(_directorySeparatorChars); - - var fullProjectPath = Path.GetFullPath(projectPath); - var fullOutputPath = string.IsNullOrEmpty(outputPath) - ? fullProjectPath - : Path.GetFullPath(Path.Combine(projectPath, outputPath)); + outputPath = string.IsNullOrWhiteSpace(outputPath) ? null : outputPath; + var subNamespace = SubnamespaceFromOutputPath(projectPath, outputPath); var @namespace = rootNamespace; - if (!string.Equals(fullOutputPath, fullProjectPath) - && fullOutputPath.StartsWith(fullProjectPath, StringComparison.Ordinal)) + + if (!string.IsNullOrEmpty(subNamespace)) { - var relativeOutputPath = fullOutputPath.Substring(fullProjectPath.Length + 1); - @namespace += "." + string.Join( - ".", relativeOutputPath - .Split(_directorySeparatorChars, StringSplitOptions.RemoveEmptyEntries) - .Select( - p => _cSharpUtilities.GenerateCSharpIdentifier( - p, - existingIdentifiers: null, - singularizePluralizer: null))); + @namespace += "." + subNamespace; } if (string.IsNullOrEmpty(contextName)) @@ -129,9 +117,27 @@ public virtual ReverseEngineerFiles Generate( } } - CheckOutputFiles(fullOutputPath, contextName, model, overwriteFiles); + CheckOutputFiles(outputPath ?? projectPath, contextName, model, overwriteFiles); + + return ScaffoldingCodeGenerator.WriteCode(model, outputPath ?? projectPath, @namespace, contextName, connectionString, useDataAnnotations); + } + + // if outputDir is a subfolder of projectDir, then use each subfolder as a subnamespace + // --output-dir $(projectFolder)/A/B/C + // => "namespace $(rootnamespace).A.B.C" + private string SubnamespaceFromOutputPath(string projectDir, string outputDir) + { + if (outputDir == null + || !outputDir.StartsWith(projectDir, StringComparison.Ordinal)) + { + return null; + } + + var subPath = outputDir.Substring(projectDir.Length); - return ScaffoldingCodeGenerator.WriteCode(model, fullOutputPath, @namespace, contextName, connectionString, useDataAnnotations); + return !string.IsNullOrWhiteSpace(subPath) + ? string.Join(".", subPath.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)) + : null; } private void CheckOutputFiles(