From ef673e4154ea255d46d88b6ef479e9f0eea0d59e Mon Sep 17 00:00:00 2001 From: AndriySvyryd Date: Wed, 18 Aug 2021 17:31:57 -0700 Subject: [PATCH] Change the default subfolder/subnamespace for compiled models to CompiledModels Handle context types in the global namespace Fixes #25058 Fixes #25059 --- .../Design/Internal/DbContextOperations.cs | 27 ++++++++++++----- .../CSharpRuntimeModelCodeGenerator.cs | 10 ++++--- .../CSharpRuntimeModelCodeGeneratorTest.cs | 29 ++++++++++--------- 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/EFCore.Design/Design/Internal/DbContextOperations.cs b/src/EFCore.Design/Design/Internal/DbContextOperations.cs index d66f5cd6d8f..fdbe9d39ada 100644 --- a/src/EFCore.Design/Design/Internal/DbContextOperations.cs +++ b/src/EFCore.Design/Design/Internal/DbContextOperations.cs @@ -138,16 +138,29 @@ public virtual string ScriptDbContext(string? contextType) /// 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 void Optimize(string? outputDir, string? modelNamespace, string? contextType) + public virtual void Optimize(string? outputDir, string? modelNamespace, string? contextTypeName) { - using var context = CreateContext(contextType); + using var context = CreateContext(contextTypeName); + var contextType = context.GetType(); var services = _servicesBuilder.Build(context); var scaffolder = services.GetRequiredService(); - outputDir = outputDir != null - ? Path.GetFullPath(Path.Combine(_projectDir, outputDir)) - : _projectDir; + if (outputDir == null) + { + var contextSubNamespace = contextType.Namespace ?? ""; + if (!string.IsNullOrEmpty(_rootNamespace) + && contextSubNamespace.StartsWith(_rootNamespace, StringComparison.Ordinal)) + { + contextSubNamespace = contextSubNamespace[_rootNamespace.Length..]; + } + + outputDir = Path.GetFullPath(Path.Combine(_projectDir, contextSubNamespace.Replace('.', Path.DirectorySeparatorChar), "CompiledModels")); + } + else + { + outputDir = Path.GetFullPath(Path.Combine(_projectDir, outputDir)); + } var finalModelNamespace = modelNamespace ?? GetNamespaceFromOutputPath(outputDir) ?? ""; @@ -156,13 +169,13 @@ public virtual void Optimize(string? outputDir, string? modelNamespace, string? outputDir, new CompiledModelCodeGenerationOptions { - ContextType = context.GetType(), + ContextType = contextType, ModelNamespace = finalModelNamespace, Language = _language, UseNullableReferenceTypes = _nullable }); - var fullName = context.GetType().ShortDisplayName() + "Model"; + var fullName = contextType.ShortDisplayName() + "Model"; if (!string.IsNullOrEmpty(modelNamespace)) { fullName = modelNamespace + "." + fullName; diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs index 8c11c285844..5707ae3e4cc 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs @@ -141,12 +141,14 @@ private string CreateModel( bool nullable) { var mainBuilder = new IndentedStringBuilder(); - var namespaces = new SortedSet(new NamespaceComparer()) { - contextType.Namespace!, + var namespaces = new SortedSet(new NamespaceComparer()) + { typeof(RuntimeModel).Namespace!, typeof(DbContextAttribute).Namespace! }; + AddNamespace(contextType, namespaces); + if (!string.IsNullOrEmpty(@namespace)) { mainBuilder @@ -1371,14 +1373,14 @@ parameters with private static void AddNamespace(Type type, ISet namespaces) { - if (type.Namespace != null) + if (!string.IsNullOrEmpty(type.Namespace)) { namespaces.Add(type.Namespace); } if (type.IsGenericType) { - foreach(var argument in type.GenericTypeArguments) + foreach (var argument in type.GenericTypeArguments) { AddNamespace(argument, namespaces); } diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs index 7f436fa4768..a4020a6efbc 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Linq.Expressions; using System.Reflection; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Cosmos.ValueGeneration.Internal; using Microsoft.EntityFrameworkCore.Design; @@ -28,6 +29,20 @@ using Newtonsoft.Json.Linq; using Xunit; +public class GlobalNamespaceContext : Microsoft.EntityFrameworkCore.Scaffolding.Internal.CSharpRuntimeModelCodeGeneratorTest.ContextBase +{ + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity("1", e => + { + e.Property("Id"); + e.HasKey("Id"); + }); + } +} + namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal { public class CSharpRuntimeModelCodeGeneratorTest @@ -122,20 +137,6 @@ public void Global_namespace_works() }); } - public class GlobalNamespaceContext : ContextBase - { - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity("1", e => - { - e.Property("Id"); - e.HasKey("Id"); - }); - } - } - [ConditionalFact] public void Throws_for_constructor_binding() {