Skip to content

Commit 2fdb4f7

Browse files
committed
Added pluralization option
1 parent a56673a commit 2fdb4f7

File tree

8 files changed

+164
-74
lines changed

8 files changed

+164
-74
lines changed

src/EntityFrameworkCore.Scaffolding.Handlebars/EntityFrameworkCore.Scaffolding.Handlebars.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@
5555

5656
<ItemGroup>
5757
<PackageReference Include="Handlebars.Net" Version="1.10.1" />
58+
<PackageReference Include="Humanizer.Core" Version="2.8.2" />
5859
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3" />
59-
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0"/>
60+
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0" />
6061
</ItemGroup>
6162

6263
<ItemGroup>

src/EntityFrameworkCore.Scaffolding.Handlebars/HandlebarsScaffoldingOptions.cs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,43 @@ namespace EntityFrameworkCore.Scaffolding.Handlebars
1414
/// </summary>
1515
public class HandlebarsScaffoldingOptions
1616
{
17+
#region Properties
18+
1719
/// <summary>
18-
/// Gets or sets which type of scaffolding should be generated.
20+
/// Gets or sets an assembly to read embedded templates from (optional).
1921
/// </summary>
20-
public ReverseEngineerOptions ReverseEngineerOptions { get; set; } = ReverseEngineerOptions.DbContextAndEntities;
22+
public Assembly EmbeddedTemplatesAssembly { get; set; }
2123

2224
/// <summary>
23-
/// Gets or sets language options for generated scaffolding.
25+
/// Gets or sets the namespace of the embedded templates to read (optional).
2426
/// </summary>
25-
public LanguageOptions LanguageOptions { get; set; }
27+
public string EmbeddedTemplatesNamespace { get; set; }
2628

2729
/// <summary>
28-
/// Gets or sets tables that should be excluded from; can include schema.
30+
/// Gets or sets tables that should be excluded from; can include schema.
2931
/// </summary>
3032
public List<string> ExcludedTables { get; set; }
3133

3234
/// <summary>
33-
/// Gets or sets Template data to pass in to template creation.
35+
/// Gets or sets language options for generated scaffolding.
3436
/// </summary>
35-
public IDictionary<string, object> TemplateData { get; set; }
37+
public LanguageOptions LanguageOptions { get; set; }
3638

3739
/// <summary>
38-
/// Gets or sets an assembly to read embedded templates from (optional).
39-
/// </summary>
40-
public Assembly EmbeddedTemplatesAssembly { get; set; }
40+
/// Gets or sets pluralizer options for generated scaffolding.
41+
/// </summary>
42+
public PluralizerOptions PluralizerOptions { get; set; }
4143

4244
/// <summary>
43-
/// Gets or sets the namespace of the embedded templates to read (optional).
45+
/// Gets or sets which type of scaffolding should be generated.
4446
/// </summary>
45-
public string EmbeddedTemplatesNamespace { get; set; }
47+
public ReverseEngineerOptions ReverseEngineerOptions { get; set; } = ReverseEngineerOptions.DbContextAndEntities;
48+
49+
/// <summary>
50+
/// Gets or sets Template data to pass in to template creation.
51+
/// </summary>
52+
public IDictionary<string, object> TemplateData { get; set; }
53+
54+
#endregion
4655
}
4756
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Humanizer;
2+
using Microsoft.EntityFrameworkCore.Design;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Text;
6+
7+
namespace EntityFrameworkCore.Scaffolding.Handlebars
8+
{
9+
/// <summary>
10+
/// Provide methods to implement IPluralizer with Humanizer.
11+
/// </summary>
12+
public class HumanizerPluralizer : IPluralizer
13+
{
14+
#region Methods
15+
16+
/// <summary>
17+
/// Pluralize a string
18+
/// </summary>
19+
/// <param name="identifier">String to pluralize</param>
20+
/// <returns></returns>
21+
public string Pluralize(string identifier)
22+
{
23+
string result = identifier.Pluralize(false);
24+
return result;
25+
}
26+
27+
/// <summary>
28+
/// Singularize a string
29+
/// </summary>
30+
/// <param name="identifier">String to singularize</param>
31+
/// <returns></returns>
32+
public string Singularize(string identifier)
33+
{
34+
string result = identifier.Singularize(false);
35+
return result;
36+
}
37+
38+
#endregion
39+
}
40+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace EntityFrameworkCore.Scaffolding.Handlebars
6+
{
7+
/// <summary>
8+
/// Pluralizer options for reverse engineering classes from an existing database.
9+
/// </summary>
10+
public enum PluralizerOptions
11+
{
12+
/// <summary>
13+
/// Don't use pluralization service
14+
/// </summary>
15+
DontUse,
16+
17+
/// <summary>
18+
/// Use humanizer implementation of pluralization
19+
/// </summary>
20+
UseHumanizerPluralization
21+
}
22+
}

src/EntityFrameworkCore.Scaffolding.Handlebars/ServiceCollectionExtensions.cs

Lines changed: 58 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,60 @@ namespace Microsoft.EntityFrameworkCore.Design
1717
/// </summary>
1818
public static class ServiceCollectionExtensions
1919
{
20+
#region Methods
21+
22+
/// <summary>
23+
/// Register Handlebars block helpers.
24+
/// <para>
25+
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsBlockHelpers.
26+
/// </para> /// </summary>
27+
/// <param name="services">The <see cref="IServiceCollection" /> to add services to. </param>
28+
/// <param name="handlebarsBlockHelpers">Handlebars block helpers.</param>
29+
/// <returns></returns>
30+
public static IServiceCollection AddHandlebarsBlockHelpers(this IServiceCollection services,
31+
params (string helperName, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]> helperFunction)[] handlebarsBlockHelpers)
32+
{
33+
services.AddSingleton<IHbsBlockHelperService>(provider =>
34+
{
35+
var helpers = new Dictionary<string, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]>>();
36+
handlebarsBlockHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
37+
return new HbsBlockHelperService(helpers);
38+
});
39+
return services;
40+
}
41+
42+
/// <summary>
43+
/// Register Handlebars helpers.
44+
/// <para>
45+
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsHelpers.
46+
/// </para>
47+
/// </summary>
48+
/// <param name="services"> The <see cref="IServiceCollection" /> to add services to. </param>
49+
/// <param name="handlebarsHelpers">Handlebars helpers.</param>
50+
/// <returns>The same service collection so that multiple calls can be chained.</returns>
51+
public static IServiceCollection AddHandlebarsHelpers(this IServiceCollection services,
52+
params (string helperName, Action<TextWriter, Dictionary<string, object>, object[]> helperFunction)[] handlebarsHelpers)
53+
{
54+
services.AddSingleton<IHbsHelperService>(provider =>
55+
{
56+
var helpers = new Dictionary<string, Action<TextWriter, Dictionary<string, object>, object[]>>
57+
{
58+
{Constants.SpacesHelper, HandlebarsHelpers.SpacesHelper}
59+
};
60+
handlebarsHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
61+
return new HbsHelperService(helpers);
62+
});
63+
return services;
64+
}
65+
2066
/// <summary>
2167
/// <para>
2268
/// Registers the Handlebars scaffolding generator as a service in the <see cref="IServiceCollection" />.
23-
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
69+
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
2470
/// templates in the CodeTemplates folder.
2571
/// </para>
2672
/// <para>
27-
/// Has <paramref name="options" /> that allow you to choose whether to generate only the DbContext class,
73+
/// Has <paramref name="options" /> that allow you to choose whether to generate only the DbContext class,
2874
/// only entity type classes, or both DbContext and entity type classes (the default).
2975
/// </para>
3076
/// </summary>
@@ -46,11 +92,11 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
4692
/// <summary>
4793
/// <para>
4894
/// Registers the Handlebars scaffolding generator as a service in the <see cref="IServiceCollection" />.
49-
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
95+
/// This allows you to customize generated DbContext and entity type classes by modifying the Handlebars
5096
/// templates in the CodeTemplates folder.
5197
/// </para>
5298
/// <para>
53-
/// Has <paramref name="configureOptions" /> that allow you to choose whether to generate only the DbContext class,
99+
/// Has <paramref name="configureOptions" /> that allow you to choose whether to generate only the DbContext class,
54100
/// only entity type classes, or both DbContext and entity type classes (the default).
55101
/// It also allows you to exclude tables from the generation.
56102
/// This can be useful when placing model classes in a separate class library.
@@ -106,6 +152,11 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
106152
services.AddSingleton<ITemplateLanguageService, CSharpTemplateLanguageService>();
107153
}
108154

155+
if (scaffoldingOptions.PluralizerOptions == PluralizerOptions.UseHumanizerPluralization)
156+
{
157+
services.AddSingleton<IPluralizer, HumanizerPluralizer>();
158+
}
159+
109160
if (scaffoldingOptions.EmbeddedTemplatesAssembly != null)
110161
{
111162
services.AddSingleton<ITemplateFileService>(new EmbeddedResourceTemplateFileService(
@@ -136,50 +187,6 @@ public static IServiceCollection AddHandlebarsScaffolding(this IServiceCollectio
136187
return services;
137188
}
138189

139-
/// <summary>
140-
/// Register Handlebars helpers.
141-
/// <para>
142-
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsHelpers.
143-
/// </para>
144-
/// </summary>
145-
/// <param name="services"> The <see cref="IServiceCollection" /> to add services to. </param>
146-
/// <param name="handlebarsHelpers">Handlebars helpers.</param>
147-
/// <returns>The same service collection so that multiple calls can be chained.</returns>
148-
public static IServiceCollection AddHandlebarsHelpers(this IServiceCollection services,
149-
params (string helperName, Action<TextWriter, Dictionary<string, object>, object[]> helperFunction)[] handlebarsHelpers)
150-
{
151-
services.AddSingleton<IHbsHelperService>(provider =>
152-
{
153-
var helpers = new Dictionary<string, Action<TextWriter, Dictionary<string, object>, object[]>>
154-
{
155-
{Constants.SpacesHelper, HandlebarsHelpers.SpacesHelper}
156-
};
157-
handlebarsHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
158-
return new HbsHelperService(helpers);
159-
});
160-
return services;
161-
}
162-
163-
/// <summary>
164-
/// Register Handlebars block helpers.
165-
/// <para>
166-
/// Note: You must first call AddHandlebarsScaffolding before calling AddHandlebarsBlockHelpers.
167-
/// </para> /// </summary>
168-
/// <param name="services">The <see cref="IServiceCollection" /> to add services to. </param>
169-
/// <param name="handlebarsBlockHelpers">Handlebars block helpers.</param>
170-
/// <returns></returns>
171-
public static IServiceCollection AddHandlebarsBlockHelpers(this IServiceCollection services,
172-
params (string helperName, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]> helperFunction)[] handlebarsBlockHelpers)
173-
{
174-
services.AddSingleton<IHbsBlockHelperService>(provider =>
175-
{
176-
var helpers = new Dictionary<string, Action<TextWriter, HelperOptions, Dictionary<string, object>, object[]>>();
177-
handlebarsBlockHelpers.ToList().ForEach(h => helpers.Add(h.helperName, h.helperFunction));
178-
return new HbsBlockHelperService(helpers);
179-
});
180-
return services;
181-
}
182-
183190
/// <summary>
184191
/// Register Handlebars transformers.
185192
/// <para>
@@ -209,5 +216,7 @@ public static IServiceCollection AddHandlebarsTransformers(this IServiceCollecti
209216
navPropertyTransformer));
210217
return services;
211218
}
219+
220+
#endregion
212221
}
213-
}
222+
}

test/Scaffolding.Handlebars.Tests/ExpectedContexts.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ namespace FakeNamespace
1313
{
1414
public partial class FakeDbContext : DbContext
1515
{
16-
public virtual DbSet<Category> Category { get; set; }
17-
public virtual DbSet<Product> Product { get; set; }
16+
public virtual DbSet<Category> Categories { get; set; }
17+
public virtual DbSet<Product> Products { get; set; }
1818
1919
public FakeDbContext(DbContextOptions<FakeDbContext> options) : base(options)
2020
{
@@ -33,13 +33,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
3333
{
3434
modelBuilder.Entity<Category>(entity =>
3535
{
36+
entity.ToTable(""Category"");
37+
3638
entity.Property(e => e.CategoryName)
3739
.IsRequired()
3840
.HasMaxLength(15);
3941
});
4042
4143
modelBuilder.Entity<Product>(entity =>
4244
{
45+
entity.ToTable(""Product"");
46+
4347
entity.HasIndex(e => e.CategoryId);
4448
4549
entity.Property(e => e.ProductName)
@@ -51,7 +55,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
5155
entity.Property(e => e.UnitPrice).HasColumnType(""money"");
5256
5357
entity.HasOne(d => d.Category)
54-
.WithMany(p => p.Product)
58+
.WithMany(p => p.Products)
5559
.HasForeignKey(d => d.CategoryId);
5660
});
5761
@@ -75,8 +79,8 @@ namespace FakeNamespace
7579
{
7680
public partial class FakeDbContext : DbContext
7781
{
78-
public virtual DbSet<Category> Category { get; set; }
79-
public virtual DbSet<Product> Product { get; set; }
82+
public virtual DbSet<Category> Categories { get; set; }
83+
public virtual DbSet<Product> Products { get; set; }
8084
8185
public FakeDbContext(DbContextOptions<FakeDbContext> options) : base(options)
8286
{

test/Scaffolding.Handlebars.Tests/ExpectedEntities.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ public partial class Category
1414
{
1515
public Category()
1616
{
17-
Product = new HashSet<Product>();
17+
Products = new HashSet<Product>();
1818
}
1919
2020
public int CategoryId { get; set; }
2121
public string CategoryName { get; set; }
2222
23-
public virtual ICollection<Product> Product { get; set; }
23+
public virtual ICollection<Product> Products { get; set; }
2424
}
2525
}
2626
";
@@ -56,11 +56,12 @@ private static class ExpectedEntitiesWithAnnotations
5656
5757
namespace FakeNamespace
5858
{
59+
[Table(""Category"")]
5960
public partial class Category
6061
{
6162
public Category()
6263
{
63-
Product = new HashSet<Product>();
64+
Products = new HashSet<Product>();
6465
}
6566
6667
[Key]
@@ -69,8 +70,8 @@ public Category()
6970
[StringLength(15)]
7071
public string CategoryName { get; set; }
7172
72-
[InverseProperty(""Category"")]
73-
public virtual ICollection<Product> Product { get; set; }
73+
[InverseProperty(nameof(Product.Category))]
74+
public virtual ICollection<Product> Products { get; set; }
7475
}
7576
}
7677
";
@@ -83,6 +84,7 @@ public Category()
8384
8485
namespace FakeNamespace
8586
{
87+
[Table(""Product"")]
8688
public partial class Product
8789
{
8890
[Key]
@@ -97,7 +99,7 @@ public partial class Product
9799
public int? CategoryId { get; set; }
98100
99101
[ForeignKey(nameof(CategoryId))]
100-
[InverseProperty(""Product"")]
102+
[InverseProperty(""Products"")]
101103
public virtual Category Category { get; set; }
102104
}
103105
}

0 commit comments

Comments
 (0)