From c7ed32a0fc10fd90ac6fe9ad9418f7f797d58038 Mon Sep 17 00:00:00 2001 From: Caelan Mayberry Date: Thu, 1 Feb 2024 11:16:43 -0800 Subject: [PATCH] Name Transformer changes updated for latest version. --- .../HbsCSharpDbContextGenerator.cs | 16 +-- .../HbsCSharpEntityTypeGenerator.cs | 13 +- .../HbsCSharpModelGenerator.cs | 2 +- .../HbsEntityTypeTransformationService2.cs | 22 +++ .../HbsEntityTypeTransformationServiceBase.cs | 32 +++++ .../HbsTypeScriptEntityTypeGenerator.cs | 11 +- .../HbsTypeScriptModelGenerator.cs | 2 +- .../IEntityTypeTransformationService.cs | 22 +++ .../ServiceCollectionExtensions.cs | 36 +++++ .../ExpectedEntities.cs | 125 ++++++++++-------- .../HbsCSharpScaffoldingGeneratorTests.cs | 114 ++++++++++++++-- .../Helpers/Constants.cs | 24 ++++ .../Helpers/HandlebarsTransformers.cs | 21 +++ 13 files changed, 352 insertions(+), 88 deletions(-) diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpDbContextGenerator.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpDbContextGenerator.cs index b2e60c1..914a8b0 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpDbContextGenerator.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpDbContextGenerator.cs @@ -291,7 +291,7 @@ private void GenerateDbSets(IModel model) } var transformedEntityTypeName = GetEntityTypeName( - entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType.Name)); + entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType, entityType.Name)); dbSets.Add(new Dictionary { { "set-property-type", transformedEntityTypeName }, @@ -327,7 +327,7 @@ private void InitializeEntityTypeBuilder(IEntityType entityType, IndentedStringB if (!_entityTypeBuilderInitialized) { var transformedEntityTypeName = GetEntityTypeName( - entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType.Name)); + entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType, entityType.Name)); sb.AppendLine(); sb.AppendLine($"modelBuilder.Entity<{transformedEntityTypeName}>({EntityLambdaIdentifier} =>"); @@ -536,7 +536,7 @@ private void GenerateTableName(IEntityType entityType, IndentedStringBuilder sb) var schema = entityType.GetSchema(); var defaultSchema = entityType.Model.GetDefaultSchema(); - var transformedTableName = EntityTypeTransformationService.TransformTypeEntityName(tableName); + var transformedTableName = EntityTypeTransformationService.TransformTypeEntityName(entityType, tableName); var tableNameVirtual = tableName != null && !tableName.Equals(transformedTableName); var explicitSchema = schema != null && schema != defaultSchema; @@ -773,7 +773,7 @@ private void GenerateRelationship(IEntityType entityType, IForeignKey foreignKey lines.Add( $".{nameof(ReferenceReferenceBuilder.HasForeignKey)}" - + (foreignKey.IsUnique ? $"<{GetEntityTypeName(entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType.Name))}>" : "") + + (foreignKey.IsUnique ? $"<{GetEntityTypeName(entityType, EntityTypeTransformationService.TransformTypeEntityName(entityType, entityType.Name))}>" : "") + $"(d => {GenerateLambdaToKey(entityType, foreignKey.Properties, "d", EntityTypeTransformationService.TransformPropertyName)})"); var defaultOnDeleteAction = foreignKey.IsRequired @@ -820,10 +820,10 @@ private void GenerateManyToMany(ISkipNavigation skipNavigation, IndentedStringBu var joinEntityType = skipNavigation.JoinEntityType; using (sb.Indent()) { - sb.AppendLine($"{EntityLambdaIdentifier}.{nameof(EntityTypeBuilder.HasMany)}(d => d.{EntityTypeTransformationService.TransformTypeEntityName(skipNavigation.Name)})"); + sb.AppendLine($"{EntityLambdaIdentifier}.{nameof(EntityTypeBuilder.HasMany)}(d => d.{EntityTypeTransformationService.TransformTypeEntityName(skipNavigation.JoinEntityType, skipNavigation.Name)})"); using (sb.Indent()) { - sb.AppendLine($".{nameof(CollectionNavigationBuilder.WithMany)}(p => p.{EntityTypeTransformationService.TransformTypeEntityName(inverse.Name)})"); + sb.AppendLine($".{nameof(CollectionNavigationBuilder.WithMany)}(p => p.{EntityTypeTransformationService.TransformTypeEntityName(inverse.DeclaringEntityType, inverse.Name)})"); sb.AppendLine( $".{nameof(CollectionCollectionBuilder.UsingEntity)}<{CSharpHelper.Reference(Model.DefaultPropertyBagType)}>("); using (sb.Indent()) @@ -831,8 +831,8 @@ private void GenerateManyToMany(ISkipNavigation skipNavigation, IndentedStringBu sb.AppendLine($"{CSharpHelper.Literal(joinEntityType.Name)},"); var lines = new List(); - var navEntityTypeName = GetEntityTypeName(inverse.ForeignKey.PrincipalEntityType, EntityTypeTransformationService.TransformTypeEntityName(inverse.ForeignKey.PrincipalEntityType.Name)); - var skipNavEntityTypeName = GetEntityTypeName(skipNavigation.ForeignKey.PrincipalEntityType, EntityTypeTransformationService.TransformTypeEntityName(skipNavigation.ForeignKey.PrincipalEntityType.Name)); + var navEntityTypeName = GetEntityTypeName(inverse.ForeignKey.PrincipalEntityType, EntityTypeTransformationService.TransformTypeEntityName(inverse.ForeignKey.PrincipalEntityType, inverse.ForeignKey.PrincipalEntityType.Name)); + var skipNavEntityTypeName = GetEntityTypeName(skipNavigation.ForeignKey.PrincipalEntityType, EntityTypeTransformationService.TransformTypeEntityName(skipNavigation.ForeignKey.PrincipalEntityType, skipNavigation.ForeignKey.PrincipalEntityType.Name)); GenerateForeignKeyConfigurationLines(inverse.ForeignKey, navEntityTypeName, "l"); GenerateForeignKeyConfigurationLines(skipNavigation.ForeignKey, skipNavEntityTypeName, "r"); sb.AppendLine("j =>"); diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpEntityTypeGenerator.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpEntityTypeGenerator.cs index 7bafb8d..e757481 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpEntityTypeGenerator.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpEntityTypeGenerator.cs @@ -189,7 +189,7 @@ protected virtual void GenerateClass(IEntityType entityType) GenerateEntityTypeDataAnnotations(entityType); } - var transformedEntityName = EntityTypeTransformationService.TransformTypeEntityName(entityType.Name); + var transformedEntityName = EntityTypeTransformationService.TransformTypeEntityName(entityType, entityType.Name); if (_options?.Value?.GenerateComments == true) TemplateData.Add("comment", GenerateComment(entityType.GetComment(), 1)); @@ -220,10 +220,11 @@ protected virtual void GenerateConstructor(IEntityType entityType) foreach (var navigation in collectionNavigations) { + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); lines.Add(new Dictionary { { "property-name", navigation.Name }, - { "property-type", navigation.TargetEntityType.Name } + { "property-type", navPropertyType } }); } @@ -335,7 +336,7 @@ protected virtual void GenerateNavigationProperties(IEntityType entityType) var propertyIsNullable = !navigation.IsCollection && UseNullableReferenceTypes && !navigation.ForeignKey.IsRequired; - var navPropertyType = navigation.TargetEntityType.Name; + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); navProperties.Add(new Dictionary { { "nav-property-collection", navigation.IsCollection }, @@ -378,7 +379,7 @@ protected virtual void GenerateSkipNavigationProperties(IEntityType entityType) var propertyIsNullable = !navigation.IsCollection && UseNullableReferenceTypes && !navigation.ForeignKey.IsRequired; - var navPropertyType = navigation.TargetEntityType.Name; + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); if (propertyIsNullable && !navPropertyType.EndsWith("?")) { navPropertyType += "?"; @@ -689,7 +690,7 @@ private void GenerateInversePropertyAttribute(IEntityType entityType, INavigatio m => m.Name == inverseNavigation.DeclaringEntityType.Name || EntityTypeTransformationService.TransformNavPropertyName(entityType, m.Name, navigation.TargetEntityType.Name) == EntityTypeTransformationService.TransformNavPropertyName(entityType, inverseNavigation.DeclaringEntityType.Name, navigation.TargetEntityType.Name)) - ? $"nameof({EntityTypeTransformationService.TransformTypeEntityName(inverseNavigation.DeclaringType.Name)}.{propertyName})" + ? $"nameof({EntityTypeTransformationService.TransformTypeEntityName(inverseNavigation.DeclaringEntityType, inverseNavigation.DeclaringType.Name)}.{propertyName})" : CSharpHelper.Literal(propertyName)); NavPropertyAnnotations.Add(new Dictionary @@ -766,7 +767,7 @@ private void GenerateInversePropertyAttribute(IEntityType entityType, ISkipNavig m => m.Name == inverseNavigation.DeclaringEntityType.Name || EntityTypeTransformationService.TransformNavPropertyName(entityType, m.Name, navigation.TargetEntityType.Name) == EntityTypeTransformationService.TransformNavPropertyName(entityType, inverseNavigation.DeclaringEntityType.Name, navigation.TargetEntityType.Name)) - ? $"nameof({EntityTypeTransformationService.TransformTypeEntityName(inverseNavigation.DeclaringType.Name)}.{propertyName})" + ? $"nameof({EntityTypeTransformationService.TransformTypeEntityName(inverseNavigation.DeclaringEntityType, inverseNavigation.DeclaringType.Name)}.{propertyName})" : CSharpHelper.Literal(propertyName)); NavPropertyAnnotations.Add(new Dictionary diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpModelGenerator.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpModelGenerator.cs index 44e5ba0..84b91b6 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpModelGenerator.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsCSharpModelGenerator.cs @@ -169,7 +169,7 @@ public virtual ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationOp options.UseDataAnnotations, options.UseNullableReferenceTypes); - var transformedFileName = EntityTypeTransformationService.TransformEntityFileName(entityType.Name); + var transformedFileName = EntityTypeTransformationService.TransformEntityFileName(entityType, entityType.Name); var schema = !string.IsNullOrEmpty(entityType.GetTableName()) ? entityType.GetSchema() : entityType.GetViewSchema(); diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationService2.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationService2.cs index d0147e5..b8a8251 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationService2.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationService2.cs @@ -52,5 +52,27 @@ public HbsEntityTypeTransformationService2( PropertyTransformer2 = propertyTransformer; NavPropertyTransformer2 = navPropertyTransformer; } + + /// + /// HbsEntityTypeTransformationService constructor. + /// + /// Entity type name transformer. + /// Entity file name transformer. + /// Constructor transformer. + /// Property name transformer. + /// Navigation property name transformer. + public HbsEntityTypeTransformationService2( + Func entityTypeNameTransformer = null, + Func entityFileNameTransformer = null, + Func constructorTransformer = null, + Func propertyTransformer = null, + Func navPropertyTransformer = null) + { + EntityTypeNameTransformer2 = entityTypeNameTransformer; + EntityFileNameTransformer2 = entityFileNameTransformer; + ConstructorTransformer2 = constructorTransformer; + PropertyTransformer2 = propertyTransformer; + NavPropertyTransformer2 = navPropertyTransformer; + } } } \ No newline at end of file diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationServiceBase.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationServiceBase.cs index 8a0be44..2764778 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationServiceBase.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsEntityTypeTransformationServiceBase.cs @@ -33,6 +33,16 @@ public abstract class HbsEntityTypeTransformationServiceBase : IEntityTypeTransf /// protected Func NavPropertyTransformer { get; set; } + /// + /// Entity name transformer. + /// + protected Func EntityTypeNameTransformer2 { get; set; } + + /// + /// Entity file name transformer. + /// + protected Func EntityFileNameTransformer2 { get; set; } + /// /// Constructor transformer. /// @@ -69,6 +79,17 @@ public HbsEntityTypeTransformationServiceBase( public string TransformTypeEntityName(string entityName) => EntityTypeNameTransformer?.Invoke(entityName) ?? entityName; + /// + /// Transform entity type name. + /// + /// Entity type. + /// Entity type name. + /// Transformed entity type name. + public string TransformTypeEntityName(IEntityType entityType, string entityName) => + EntityTypeNameTransformer2?.Invoke(entityType, entityName) + ?? EntityTypeNameTransformer?.Invoke(entityName) + ?? entityName; + /// /// Transform entity file name. /// @@ -77,6 +98,17 @@ public string TransformTypeEntityName(string entityName) => public string TransformEntityFileName(string entityFileName) => EntityFileNameTransformer?.Invoke(entityFileName) ?? entityFileName; + /// + /// Transform entity file name. + /// + /// Entity type. + /// Entity file name. + /// Transformed entity file name. + public string TransformEntityFileName(IEntityType entityType, string entityFileName) => + EntityFileNameTransformer2?.Invoke(entityType, entityFileName) + ?? EntityFileNameTransformer?.Invoke(entityFileName) + ?? entityFileName; + /// /// Transform single property name. /// diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptEntityTypeGenerator.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptEntityTypeGenerator.cs index 9a26c6a..5fad1ae 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptEntityTypeGenerator.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptEntityTypeGenerator.cs @@ -131,7 +131,7 @@ protected virtual void GenerateClass(IEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); - var transformedEntityName = EntityTypeTransformationService.TransformTypeEntityName(entityType.Name); + var transformedEntityName = EntityTypeTransformationService.TransformTypeEntityName(entityType, entityType.Name); TemplateData.Add("comment", entityType.GetComment()); TemplateData.Add("class", transformedEntityName); @@ -158,10 +158,11 @@ protected virtual void GenerateConstructor(IEntityType entityType) foreach (var navigation in collectionNavigations) { + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); lines.Add(new Dictionary { { "property-name", navigation.Name }, - { "property-type", navigation.TargetEntityType.Name }, + { "property-type", navPropertyType }, }); } @@ -219,10 +220,11 @@ protected virtual void GenerateNavigationProperties(IEntityType entityType) foreach (var navigation in sortedNavigations) { + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); navProperties.Add(new Dictionary { { "nav-property-collection", navigation.IsCollection }, - { "nav-property-type", navigation.TargetEntityType.Name }, + { "nav-property-type", navPropertyType }, { "nav-property-name", TypeScriptHelper.ToCamelCase(navigation.Name) }, { "nav-property-annotations", new List>() }, { "nav-property-isnullable", false }, @@ -254,10 +256,11 @@ protected virtual void GenerateSkipNavigationProperties(IEntityType entityType) foreach (var navigation in sortedNavigations) { + var navPropertyType = EntityTypeTransformationService.TransformTypeEntityName(navigation.TargetEntityType, navigation.TargetEntityType.Name); navProperties.Add(new Dictionary { { "nav-property-collection", navigation.IsCollection }, - { "nav-property-type", navigation.TargetEntityType.Name }, + { "nav-property-type", navPropertyType }, { "nav-property-name", TypeScriptHelper.ToCamelCase(navigation.Name) }, { "nav-property-annotations", new List>() }, { "nav-property-isnullable", false }, diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptModelGenerator.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptModelGenerator.cs index 2dff8d1..f982133 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptModelGenerator.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/HbsTypeScriptModelGenerator.cs @@ -157,7 +157,7 @@ public virtual ScaffoldedModel GenerateModel(IModel model, ModelCodeGenerationOp options.UseDataAnnotations, options.UseNullableReferenceTypes); - var transformedFileName = EntityTypeTransformationService.TransformEntityFileName(entityType.Name); + var transformedFileName = EntityTypeTransformationService.TransformEntityFileName(entityType, entityType.Name); var entityTypeFileName = transformedFileName + FileExtension; if (_options?.Value?.EnableSchemaFolders == true) { entityTypeFileName = entityType.GetSchema() + @"\" + entityTypeFileName; diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/IEntityTypeTransformationService.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/IEntityTypeTransformationService.cs index 65a295c..7490be7 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/IEntityTypeTransformationService.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/IEntityTypeTransformationService.cs @@ -15,6 +15,17 @@ public interface IEntityTypeTransformationService /// Transformed entity type name. string TransformTypeEntityName(string entityName); + /// + /// Transform entity type name. + /// + /// Entity type. + /// Entity type name. + /// Transformed entity type name. + string TransformTypeEntityName(IEntityType entityType, string entityName) + { + return TransformTypeEntityName(entityName); + } + /// /// Transform entity file name. /// @@ -22,6 +33,17 @@ public interface IEntityTypeTransformationService /// Transformed entity file name. string TransformEntityFileName(string entityFileName); + /// + /// Transform entity file name. + /// + /// Entity type. + /// Entity file name. + /// Transformed entity file name. + string TransformEntityFileName(IEntityType entityType, string entityFileName) + { + return TransformEntityFileName(entityFileName); + } + /// /// Transform single property name. /// diff --git a/src/EntityFrameworkCore.Scaffolding.Handlebars/ServiceCollectionExtensions.cs b/src/EntityFrameworkCore.Scaffolding.Handlebars/ServiceCollectionExtensions.cs index 3d3ac1f..0c9f187 100644 --- a/src/EntityFrameworkCore.Scaffolding.Handlebars/ServiceCollectionExtensions.cs +++ b/src/EntityFrameworkCore.Scaffolding.Handlebars/ServiceCollectionExtensions.cs @@ -6,6 +6,7 @@ using EntityFrameworkCore.Scaffolding.Handlebars.Internal; using HandlebarsDotNet; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Scaffolding; using Microsoft.Extensions.DependencyInjection; using HandlebarsLib = HandlebarsDotNet.Handlebars; @@ -251,5 +252,40 @@ public static IServiceCollection AddHandlebarsTransformers2(this IServiceCollect new HbsContextTransformationService(contextFileNameTransformer)); return services; } + + /// + /// Register Handlebars transformers. + /// + /// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsTransformers. + /// This overload surfaces IEntityType in the transformers. + /// + /// + /// The to add services to. + /// Entity name transformer. + /// Entity file name transformer. + /// + /// Property name transformer. + /// Navigation property name transformer. + /// Context file name transformer. + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddHandlebarsEntityTypeTransformers(this IServiceCollection services, + Func entityTypeNameTransformer = null, + Func entityFileNameTransformer = null, + Func constructorTransformer = null, + Func propertyTransformer = null, + Func navPropertyTransformer = null, + Func contextFileNameTransformer = null) + { + services.AddSingleton(provider => + new HbsEntityTypeTransformationService2( + entityTypeNameTransformer, + entityFileNameTransformer, + constructorTransformer, + propertyTransformer, + navPropertyTransformer)); + services.AddSingleton(provider => + new HbsContextTransformationService(contextFileNameTransformer)); + return services; + } } } \ No newline at end of file diff --git a/test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs b/test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs index e83de4b..fb879c6 100644 --- a/test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs +++ b/test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs @@ -1,4 +1,6 @@ -namespace Scaffolding.Handlebars.Tests +using Scaffolding.Handlebars.Tests.Helpers; + +namespace Scaffolding.Handlebars.Tests { public partial class HbsCSharpScaffoldingGeneratorTests { @@ -55,53 +57,59 @@ public partial class Product private static class ExpectedEntitiesWithTransformMappings { - public const string CategoryClass = + public const string CategoryClassConst = @"using System; using System.Collections.Generic; namespace FakeNamespace -{ +{{ /// /// A category of products /// - public partial class CategoryRenamed - { - public CategoryRenamed() - { - Products = new HashSet(); - } + public partial class {0} + {{ + public {0}() + {{ + Products = new HashSet<{1}>(); + }} - public int CategoryIdRenamed { get; set; } + public int CategoryIdRenamed {{ get; set; }} /// /// The name of a category /// - public string CategoryNameRenamed { get; set; } + public string CategoryNameRenamed {{ get; set; }} - public virtual ICollection Products { get; set; } - } -} + public virtual ICollection<{1}> Products {{ get; set; }} + }} +}} "; - public const string ProductClass = + public const string ProductClassConst = @"using System; using System.Collections.Generic; namespace FakeNamespace -{ - public partial class ProductRenamed - { - public int ProductIdRenamed { get; set; } - public string ProductName { get; set; } - public decimal? UnitPriceRenamed { get; set; } - public bool Discontinued { get; set; } - public byte[] RowVersion { get; set; } - public int? CategoryIdRenamed { get; set; } - - public virtual CategoryRenamed Category { get; set; } - } -} +{{ + public partial class {0} + {{ + public int ProductIdRenamed {{ get; set; }} + public string ProductName {{ get; set; }} + public decimal? UnitPriceRenamed {{ get; set; }} + public bool Discontinued {{ get; set; }} + public byte[] RowVersion {{ get; set; }} + public int? CategoryIdRenamed {{ get; set; }} + + public virtual {1} Category {{ get; set; }} + }} +}} "; + + public static readonly string CategoryClass = string.Format(CategoryClassConst, Constants.Names.Transformed.Category, Constants.Names.Transformed.Product); + public static readonly string CategoryClassTransformed2 = string.Format(CategoryClassConst, Constants.Names.Transformed2.Category, Constants.Names.Transformed2.Product); + + public static readonly string ProductClass = string.Format(ProductClassConst, Constants.Names.Transformed.Product, Constants.Names.Transformed.Category); + public static readonly string ProductClassTransformed2 = string.Format(ProductClassConst, Constants.Names.Transformed2.Product, Constants.Names.Transformed2.Category); } private static class ExpectedEntitiesNoEncoding @@ -230,7 +238,7 @@ public partial class Product private static class ExpectedEntitiesWithAnnotationsAndTransformMappings { - public const string CategoryClass = + public const string CategoryClassConst = @"using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -238,21 +246,21 @@ private static class ExpectedEntitiesWithAnnotationsAndTransformMappings using Microsoft.EntityFrameworkCore; namespace FakeNamespace -{ +{{ /// /// A category of products /// [Table(""Category"")] - public partial class CategoryRenamed - { - public CategoryRenamed() - { - Products = new HashSet(); - } + public partial class {0} + {{ + public {0}() + {{ + Products = new HashSet<{1}>(); + }} [Key] [Column(""CategoryId"")] - public int CategoryIdRenamed { get; set; } + public int CategoryIdRenamed {{ get; set; }} /// /// The name of a category @@ -260,15 +268,15 @@ public CategoryRenamed() [Required] [Column(""CategoryName"")] [StringLength(15)] - public string CategoryNameRenamed { get; set; } + public string CategoryNameRenamed {{ get; set; }} - [InverseProperty(nameof(ProductRenamed.Category))] - public virtual ICollection Products { get; set; } - } -} + [InverseProperty(nameof({1}.Category))] + public virtual ICollection<{1}> Products {{ get; set; }} + }} +}} "; - public const string ProductClass = + public const string ProductClassConst = @"using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -276,30 +284,37 @@ public CategoryRenamed() using Microsoft.EntityFrameworkCore; namespace FakeNamespace -{ +{{ [Table(""Product"")] [Index(nameof(CategoryId), Name = ""IX_Product_CategoryId"")] - public partial class ProductRenamed - { + public partial class {0} + {{ [Key] [Column(""ProductId"")] - public int ProductIdRenamed { get; set; } + public int ProductIdRenamed {{ get; set; }} [Required] [StringLength(40)] - public string ProductName { get; set; } + public string ProductName {{ get; set; }} [Column(""UnitPrice"", TypeName = ""money"")] - public decimal? UnitPriceRenamed { get; set; } - public bool Discontinued { get; set; } - public byte[] RowVersion { get; set; } + public decimal? UnitPriceRenamed {{ get; set; }} + public bool Discontinued {{ get; set; }} + public byte[] RowVersion {{ get; set; }} [Column(""CategoryId"")] - public int? CategoryIdRenamed { get; set; } + public int? CategoryIdRenamed {{ get; set; }} [ForeignKey(nameof(CategoryIdRenamed))] [InverseProperty(""Products"")] - public virtual CategoryRenamed Category { get; set; } - } -} + public virtual {1} Category {{ get; set; }} + }} +}} "; + + public static readonly string CategoryClass = string.Format(CategoryClassConst, Constants.Names.Transformed.Category, Constants.Names.Transformed.Product); + public static readonly string CategoryClassTransformed2 = string.Format(CategoryClassConst, Constants.Names.Transformed2.Category, Constants.Names.Transformed2.Product); + + public static readonly string ProductClass = string.Format(ProductClassConst, Constants.Names.Transformed.Product, Constants.Names.Transformed.Category); + public static readonly string ProductClassTransformed2 = string.Format(ProductClassConst, Constants.Names.Transformed2.Product, Constants.Names.Transformed2.Category); + } private static class ExpectedEntitiesWithAnnotationsNoEncoding diff --git a/test/Scaffolding.Handlebars.Tests/HbsCSharpScaffoldingGeneratorTests.cs b/test/Scaffolding.Handlebars.Tests/HbsCSharpScaffoldingGeneratorTests.cs index b11d6a0..21a5152 100644 --- a/test/Scaffolding.Handlebars.Tests/HbsCSharpScaffoldingGeneratorTests.cs +++ b/test/Scaffolding.Handlebars.Tests/HbsCSharpScaffoldingGeneratorTests.cs @@ -18,6 +18,7 @@ using Xunit; using HandlebarsLib = HandlebarsDotNet.Handlebars; using Constants = Scaffolding.Handlebars.Tests.Helpers.Constants; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Scaffolding.Handlebars.Tests { @@ -376,6 +377,51 @@ public void WriteCode_WithTransformMapping_Should_Generate_Entity_Files(bool use Assert.Equal(expectedProduct, product); } + [Theory] + [InlineData(false, "en-US")] + [InlineData(true, "en-US")] + [InlineData(false, "tr-TR")] + [InlineData(true, "tr-TR")] + public void WriteCode_WithTransformMapping2_Should_Generate_Entity_Files(bool useDataAnnotations, string culture) + { + // Arrange + bool useTransformers2 = true; + Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); + var options = ReverseEngineerOptions.EntitiesOnly; + var scaffolder = CreateScaffolder(options, false, useTransformers2); + + // Act + var model = scaffolder.ScaffoldModel( + connectionString: ConnectionString, + databaseOptions: new DatabaseModelFactoryOptions(), + modelOptions: new ModelReverseEngineerOptions(), + codeOptions: new ModelCodeGenerationOptions + { + ContextNamespace = Constants.Parameters.RootNamespace, + ModelNamespace = Constants.Parameters.RootNamespace, + ContextName = Constants.Parameters.ContextName, + ContextDir = Constants.Parameters.ProjectPath, + UseDataAnnotations = useDataAnnotations, + Language = "C#", + }); + + // Assert + var files = GetGeneratedFiles2(model, options); + var category = files[Constants.Files.CSharpFiles.CategoryFileTransformed2]; + var product = files[Constants.Files.CSharpFiles.ProductFileTransformed2]; + + object expectedCategory; + object expectedProduct; + expectedCategory = useDataAnnotations + ? ExpectedEntitiesWithAnnotationsAndTransformMappings.CategoryClassTransformed2 + : ExpectedEntitiesWithTransformMappings.CategoryClassTransformed2; + expectedProduct = useDataAnnotations + ? ExpectedEntitiesWithAnnotationsAndTransformMappings.ProductClassTransformed2 + : ExpectedEntitiesWithTransformMappings.ProductClassTransformed2; + Assert.Equal(expectedCategory, category); + Assert.Equal(expectedProduct, product); + } + [Theory] [InlineData("en-US")] [InlineData("tr-TR")] @@ -608,18 +654,22 @@ public void Save_Should_Write_Context_and_Entity_Files() Assert.Equal(expectedProductPath, result.AdditionalFiles[2]); } - private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEngOptions, bool useEntityNameMappings) + private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEngOptions, bool useEntityNameMappings, bool useEntityNameMappings2 = false) { - return CreateScaffolder(revEngOptions, _ => { }, useEntityNameMappings, null); + return CreateScaffolder(revEngOptions, _ => { }, useEntityNameMappings, filenamePrefix: null, useEntityTransformMappings2: useEntityNameMappings2); } + private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEngOptions, string filenamePrefix = null) { - return CreateScaffolder(revEngOptions, _ => { }, false, filenamePrefix); + return CreateScaffolder(revEngOptions, _ => { }, false, filenamePrefix: filenamePrefix); } private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEngOptions, - Action configureOptions, bool useEntityTransformMappings = false, - string filenamePrefix = null, bool useAltTemplates = false) + Action configureOptions, + bool useEntityTransformMappings = false, + string filenamePrefix = null, + bool useAltTemplates = false, + bool useEntityTransformMappings2 = false) { HandlebarsLib.Configuration.NoEscape = true; var fileService = new InMemoryTemplateFileService(); @@ -683,14 +733,30 @@ private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEn // Add Transformation Services services - .AddSingleton(y => new HbsContextTransformationService(contextName => !string.IsNullOrWhiteSpace(filenamePrefix) ? $"{filenamePrefix}{contextName}" : contextName)) - .AddSingleton(y => new HbsEntityTypeTransformationService( - entityFileNameTransformer: entityName => !string.IsNullOrWhiteSpace(filenamePrefix) ? $"{filenamePrefix}{entityName}" : entityName, - entityTypeNameTransformer: entityName => useEntityTransformMappings ? HandlebarsTransformers.MapEntityName(entityName) : entityName, - constructorTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapPropertyInfo(epi) : epi, - propertyTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapPropertyInfo(epi) : epi, - navPropertyTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapNavPropertyInfo(epi) : epi - )); + .AddSingleton(y => new HbsContextTransformationService(contextName => !string.IsNullOrWhiteSpace(filenamePrefix) ? $"{filenamePrefix}{contextName}" : contextName)); + + if (useEntityTransformMappings2) + { + services + .AddSingleton(y => new HbsEntityTypeTransformationService2( + entityFileNameTransformer: (entityType, entityName) => HandlebarsTransformers.MapEntityName2(entityType, !string.IsNullOrWhiteSpace(filenamePrefix) ? $"{filenamePrefix}{entityName}" : entityName), + entityTypeNameTransformer: (entityType, entityName) => HandlebarsTransformers.MapEntityName2(entityType, entityName), + constructorTransformer: (entityType, epi) => HandlebarsTransformers.MapPropertyInfo2(entityType, epi), + propertyTransformer: (entityType, epi) => HandlebarsTransformers.MapPropertyInfo2(entityType, epi), + navPropertyTransformer: (entityType, epi) => HandlebarsTransformers.MapNavPropertyInfo2(entityType, epi) + )); + } + else + { + services + .AddSingleton(y => new HbsEntityTypeTransformationService( + entityFileNameTransformer: entityName => !string.IsNullOrWhiteSpace(filenamePrefix) ? $"{filenamePrefix}{entityName}" : entityName, + entityTypeNameTransformer: entityName => useEntityTransformMappings ? HandlebarsTransformers.MapEntityName(entityName) : entityName, + constructorTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapPropertyInfo(epi) : epi, + propertyTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapPropertyInfo(epi) : epi, + navPropertyTransformer: epi => useEntityTransformMappings ? HandlebarsTransformers.MapNavPropertyInfo(epi) : epi + )); + } services.Configure(configureOptions); @@ -702,6 +768,7 @@ private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions revEn .GetRequiredService(); return scaffolder; } + private Dictionary GetGeneratedFiles(ScaffoldedModel model, ReverseEngineerOptions options) { var generatedFiles = new Dictionary(); @@ -722,5 +789,26 @@ private Dictionary GetGeneratedFiles(ScaffoldedModel model, Reve return generatedFiles; } + + private Dictionary GetGeneratedFiles2(ScaffoldedModel model, ReverseEngineerOptions options) + { + var generatedFiles = new Dictionary(); + + if (options == ReverseEngineerOptions.DbContextOnly + || options == ReverseEngineerOptions.DbContextAndEntities) + { + generatedFiles.Add(Constants.Files.CSharpFiles.DbContextFile, model.ContextFile.Code); + } + + if (options == ReverseEngineerOptions.EntitiesOnly + || options == ReverseEngineerOptions.DbContextAndEntities) + { + generatedFiles.Add(Constants.Files.CSharpFiles.CategoryFileTransformed2, model.AdditionalFiles[0].Code); + generatedFiles.Add(Constants.Files.CSharpFiles.CustomerFileTransformed2, model.AdditionalFiles[1].Code); + generatedFiles.Add(Constants.Files.CSharpFiles.ProductFileTransformed2, model.AdditionalFiles[2].Code); + } + + return generatedFiles; + } } } \ No newline at end of file diff --git a/test/Scaffolding.Handlebars.Tests/Helpers/Constants.cs b/test/Scaffolding.Handlebars.Tests/Helpers/Constants.cs index 8ce50f1..d1beb3b 100644 --- a/test/Scaffolding.Handlebars.Tests/Helpers/Constants.cs +++ b/test/Scaffolding.Handlebars.Tests/Helpers/Constants.cs @@ -46,14 +46,38 @@ public static class TypeScriptTemplateDirectories } } + public static class Names + { + public const string Category = "Category"; + public const string Customer = "Customer"; + public const string Product = "Product"; + + public static class Transformed + { + public const string Category = Names.Category + "Renamed"; + public const string Customer = Names.Customer + "Renamed"; + public const string Product = Names.Product + "Renamed"; + } + + public static class Transformed2 + { + public const string Category = "dbo_" + Transformed.Category; + public const string Customer = "dbo_" + Transformed.Customer; + public const string Product = "dbo_" + Transformed.Product; + } + } + public static class Files { public static class CSharpFiles { public const string DbContextFile = Parameters.ContextName + ".cs"; public const string CategoryFile = "Category.cs"; + public const string CategoryFileTransformed2 = "dbo_" + CategoryFile; public const string CustomerFile = "Customer.cs"; + public const string CustomerFileTransformed2 = "dbo_" + CustomerFile; public const string ProductFile = "Product.cs"; + public const string ProductFileTransformed2 = "dbo_" + ProductFile; } public static class TypeScriptFiles { diff --git a/test/Scaffolding.Handlebars.Tests/Helpers/HandlebarsTransformers.cs b/test/Scaffolding.Handlebars.Tests/Helpers/HandlebarsTransformers.cs index c124ac0..b9abf4a 100644 --- a/test/Scaffolding.Handlebars.Tests/Helpers/HandlebarsTransformers.cs +++ b/test/Scaffolding.Handlebars.Tests/Helpers/HandlebarsTransformers.cs @@ -1,5 +1,8 @@ +using System; using EntityFrameworkCore.Scaffolding.Handlebars; using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; namespace Scaffolding.Handlebars.Tests.Helpers { @@ -11,6 +14,14 @@ public static class HandlebarsTransformers { "Customer","CustomerRenamed" }, { "Category", "CategoryRenamed" } }; + + static readonly Dictionary> _entityTypeNameMappings2 = new() + { + { "Product", (entityType, _) => entityType.GetSchema() + "_ProductRenamed" }, + { "Customer", (entityType, _) => entityType.GetSchema() + "_CustomerRenamed" }, + { "Category", (entityType, _) => entityType.GetSchema() + "_CategoryRenamed" } + }; + static readonly Dictionary _entityPropertyNameMappings = new() { { "ProductId", "ProductIdRenamed" }, @@ -18,15 +29,25 @@ public static class HandlebarsTransformers { "CategoryId", "CategoryIdRenamed" }, { "CategoryName","CategoryNameRenamed" } }; + public static string MapEntityName(string entityName) => _entityTypeNameMappings.TryGetValue(entityName, out var nameOverride) ? nameOverride : entityName; + public static string MapEntityName2(IEntityType entityType, string entityName) => + _entityTypeNameMappings2.TryGetValue(entityName, out var nameOverride) ? nameOverride(entityType, entityName) : entityName; + public static EntityPropertyInfo MapNavPropertyInfo(EntityPropertyInfo e) => new(MapPropertyTypeName(e.PropertyType), MapPropertyName(e.PropertyName)); + public static EntityPropertyInfo MapNavPropertyInfo2(IEntityType entityType, EntityPropertyInfo e) => + new(MapEntityName2(entityType, e.PropertyType), MapPropertyName(e.PropertyName)); + public static EntityPropertyInfo MapPropertyInfo(EntityPropertyInfo e) => new(MapPropertyTypeName(e.PropertyType), MapPropertyName(e.PropertyName)); + public static EntityPropertyInfo MapPropertyInfo2(IEntityType entityType, EntityPropertyInfo e) => + new(MapEntityName2(entityType, e.PropertyType), MapPropertyName(e.PropertyName)); + private static string MapPropertyTypeName(string propertyTypeName) => _entityTypeNameMappings.TryGetValue(propertyTypeName, out var propertyTypeNameOverride) ? propertyTypeNameOverride : propertyTypeName;