Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@

<ItemGroup>
<PackageReference Include="Handlebars.Net" Version="1.10.1" />
<PackageReference Include="Humanizer.Core" Version="2.8.2" />
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ public class HandlebarsScaffoldingOptions
/// </summary>
public LanguageOptions LanguageOptions { get; set; }

/// <summary>
/// Gets or sets pluralizer options for generated scaffolding.
/// </summary>
public PluralizerOptions PluralizerOptions { get; set; }

/// <summary>
/// Gets or sets tables that should be excluded from; can include schema.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Humanizer;
using Microsoft.EntityFrameworkCore.Design;
using System;
using System.Collections.Generic;
using System.Text;

namespace EntityFrameworkCore.Scaffolding.Handlebars
{
/// <summary>
/// Provide methods to implement IPluralizer with Humanizer.
/// </summary>
public class HumanizerPluralizer : IPluralizer
{
#region Methods

/// <summary>
/// Pluralize a string
/// </summary>
/// <param name="identifier">String to pluralize</param>
/// <returns></returns>
public string Pluralize(string identifier)
{
return identifier.Pluralize(false);
}

/// <summary>
/// Singularize a string
/// </summary>
/// <param name="identifier">String to singularize</param>
/// <returns></returns>
public string Singularize(string identifier)
{
return identifier.Singularize(false);
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace EntityFrameworkCore.Scaffolding.Handlebars
{
/// <summary>
/// Pluralizer options for reverse engineering classes from an existing database.
/// </summary>
public enum PluralizerOptions
{
/// <summary>
/// Don't use pluralization service
/// </summary>
DontUse,

/// <summary>
/// Use humanizer implementation of pluralization
/// </summary>
UseHumanizerPluralization
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,60 @@ namespace Microsoft.EntityFrameworkCore.Design
/// </summary>
public static class ServiceCollectionExtensions
{
#region Methods

/// <summary>
/// Register Handlebars block helpers.
/// <para>
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsBlockHelpers.
/// </para> /// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="handlebarsBlockHelpers">Handlebars block helpers.</param>
/// <returns></returns>
public static IServiceCollection AddHandlebarsBlockHelpers(this IServiceCollection services,
params (string helperName, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]> helperFunction)[] handlebarsBlockHelpers)
{
services.AddSingleton<IHbsBlockHelperService>(provider =>
{
var helpers = new Dictionary<string, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]>>();
handlebarsBlockHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
return new HbsBlockHelperService(helpers);
});
return services;
}

/// <summary>
/// Register Handlebars helpers.
/// <para>
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsHelpers.
/// </para>
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="handlebarsHelpers">Handlebars helpers.</param>
/// <returns>The same service collection so that multiple calls can be chained.</returns>
public static IServiceCollection AddHandlebarsHelpers(this IServiceCollection services,
params (string helperName, Action<TextWriter, Dictionary<string, object>, object[]> helperFunction)[] handlebarsHelpers)
{
services.AddSingleton<IHbsHelperService>(provider =>
{
var helpers = new Dictionary<string, Action<TextWriter, Dictionary<string, object>, object[]>>
{
{Constants.SpacesHelper, HandlebarsHelpers.SpacesHelper}
};
handlebarsHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
return new HbsHelperService(helpers);
});
return services;
}

/// <summary>
/// <para>
/// Registers the Handlebars scaffolding generator as a service in the <see cref="IServiceCollection" />.
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
/// templates in the CodeTemplates folder.
/// </para>
/// <para>
/// Has <paramref name="options" /> that allow you to choose whether to generate only the DbContext class,
/// Has <paramref name="options" /> that allow you to choose whether to generate only the DbContext class,
/// only entity type classes, or both DbContext and entity type classes (the default).
/// </para>
/// </summary>
Expand All @@ -46,11 +92,11 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
/// <summary>
/// <para>
/// Registers the Handlebars scaffolding generator as a service in the <see cref="IServiceCollection" />.
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
/// templates in the CodeTemplates folder.
/// </para>
/// <para>
/// Has <paramref name="configureOptions" /> that allow you to choose whether to generate only the DbContext class,
/// Has <paramref name="configureOptions" /> that allow you to choose whether to generate only the DbContext class,
/// only entity type classes, or both DbContext and entity type classes (the default).
/// It also allows you to exclude tables from the generation.
/// This can be useful when placing model classes in a separate class library.
Expand Down Expand Up @@ -106,6 +152,11 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
services.AddSingleton<ITemplateLanguageService, CSharpTemplateLanguageService>();
}

if (scaffoldingOptions.PluralizerOptions == PluralizerOptions.UseHumanizerPluralization)
{
services.AddSingleton<IPluralizer, HumanizerPluralizer>();
}

if (scaffoldingOptions.EmbeddedTemplatesAssembly != null)
{
services.AddSingleton<ITemplateFileService>(new EmbeddedResourceTemplateFileService(
Expand Down Expand Up @@ -136,50 +187,6 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
return services;
}

/// <summary>
/// Register Handlebars helpers.
/// <para>
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsHelpers.
/// </para>
/// </summary>
/// <param name="services"> The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="handlebarsHelpers">Handlebars helpers.</param>
/// <returns>The same service collection so that multiple calls can be chained.</returns>
public static IServiceCollection AddHandlebarsHelpers(this IServiceCollection services,
params (string helperName, Action<TextWriter, Dictionary<string, object>, object[]> helperFunction)[] handlebarsHelpers)
{
services.AddSingleton<IHbsHelperService>(provider =>
{
var helpers = new Dictionary<string, Action<TextWriter, Dictionary<string, object>, object[]>>
{
{Constants.SpacesHelper, HandlebarsHelpers.SpacesHelper}
};
handlebarsHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
return new HbsHelperService(helpers);
});
return services;
}

/// <summary>
/// Register Handlebars block helpers.
/// <para>
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsBlockHelpers.
/// </para> /// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to. </param>
/// <param name="handlebarsBlockHelpers">Handlebars block helpers.</param>
/// <returns></returns>
public static IServiceCollection AddHandlebarsBlockHelpers(this IServiceCollection services,
params (string helperName, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]> helperFunction)[] handlebarsBlockHelpers)
{
services.AddSingleton<IHbsBlockHelperService>(provider =>
{
var helpers = new Dictionary<string, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]>>();
handlebarsBlockHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
return new HbsBlockHelperService(helpers);
});
return services;
}

/// <summary>
/// Register Handlebars transformers.
/// <para>
Expand Down Expand Up @@ -209,5 +216,7 @@ public static IServiceCollection AddHandlebarsTransformers(this IServiceCollecti
navPropertyTransformer));
return services;
}

#endregion
}
}
}
14 changes: 9 additions & 5 deletions test/Scaffolding.Handlebars.Tests/ExpectedContexts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace FakeNamespace
{
public partial class FakeDbContext : DbContext
{
public virtual DbSet<Category> Category { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Product> Products { get; set; }

public FakeDbContext(DbContextOptions<FakeDbContext> options) : base(options)
{
Expand All @@ -33,13 +33,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Category>(entity =>
{
entity.ToTable(""Category"");

entity.Property(e => e.CategoryName)
.IsRequired()
.HasMaxLength(15);
});

modelBuilder.Entity<Product>(entity =>
{
entity.ToTable(""Product"");

entity.HasIndex(e => e.CategoryId);

entity.Property(e => e.ProductName)
Expand All @@ -51,7 +55,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
entity.Property(e => e.UnitPrice).HasColumnType(""money"");

entity.HasOne(d => d.Category)
.WithMany(p => p.Product)
.WithMany(p => p.Products)
.HasForeignKey(d => d.CategoryId);
});

Expand All @@ -75,8 +79,8 @@ namespace FakeNamespace
{
public partial class FakeDbContext : DbContext
{
public virtual DbSet<Category> Category { get; set; }
public virtual DbSet<Product> Product { get; set; }
public virtual DbSet<Category> Categories { get; set; }
public virtual DbSet<Product> Products { get; set; }

public FakeDbContext(DbContextOptions<FakeDbContext> options) : base(options)
{
Expand Down
14 changes: 8 additions & 6 deletions test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ public partial class Category
{
public Category()
{
Product = new HashSet<Product>();
Products = new HashSet<Product>();
}

public int CategoryId { get; set; }
public string CategoryName { get; set; }

public virtual ICollection<Product> Product { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
";
Expand Down Expand Up @@ -56,11 +56,12 @@ private static class ExpectedEntitiesWithAnnotations

namespace FakeNamespace
{
[Table(""Category"")]
public partial class Category
{
public Category()
{
Product = new HashSet<Product>();
Products = new HashSet<Product>();
}

[Key]
Expand All @@ -69,8 +70,8 @@ public Category()
[StringLength(15)]
public string CategoryName { get; set; }

[InverseProperty(""Category"")]
public virtual ICollection<Product> Product { get; set; }
[InverseProperty(nameof(Product.Category))]
public virtual ICollection<Product> Products { get; set; }
}
}
";
Expand All @@ -83,6 +84,7 @@ public Category()

namespace FakeNamespace
{
[Table(""Product"")]
public partial class Product
{
[Key]
Expand All @@ -97,7 +99,7 @@ public partial class Product
public int? CategoryId { get; set; }

[ForeignKey(nameof(CategoryId))]
[InverseProperty(""Product"")]
[InverseProperty(""Products"")]
public virtual Category Category { get; set; }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ public void Save_Should_Write_Context_and_Entity_Files()
}
}

private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions options)
private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions options, PluralizerOptions pluralizerOptions = PluralizerOptions.UseHumanizerPluralization)
{
var fileService = new InMemoryTemplateFileService();
fileService.InputFiles(ContextClassTemplate, ContextImportsTemplate, ContextCtorTemplate, ContextDbSetsTemplate,
Expand Down Expand Up @@ -389,6 +389,9 @@ private IReverseEngineerScaffolder CreateScaffolder(ReverseEngineerOptions optio
new HbsBlockHelperService(new Dictionary<string, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]>>()))
.AddSingleton<IReverseEngineerScaffolder, HbsReverseEngineerScaffolder>();

if (pluralizerOptions == PluralizerOptions.UseHumanizerPluralization)
services.AddSingleton<IPluralizer, HumanizerPluralizer>();

new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
var scaffolder = services
.BuildServiceProvider()
Expand Down