{"id":21908631,"url":"https://github.com/trackableentities/entityframeworkcore.scaffolding.handlebars","last_synced_at":"2026-04-21T08:07:21.925Z","repository":{"id":37602878,"uuid":"107029167","full_name":"TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars","owner":"TrackableEntities","description":"Scaffold EF Core models using Handlebars templates.","archived":false,"fork":false,"pushed_at":"2025-03-23T22:19:36.000Z","size":1785,"stargazers_count":216,"open_issues_count":10,"forks_count":52,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-13T16:53:25.195Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TrackableEntities.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-10-15T16:31:11.000Z","updated_at":"2025-03-23T22:17:34.000Z","dependencies_parsed_at":"2024-05-29T14:44:41.384Z","dependency_job_id":"3b78bcb2-3e7e-4ed8-8fea-176e9c08c4d6","html_url":"https://github.com/TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars","commit_stats":{"total_commits":185,"total_committers":21,"mean_commits":8.80952380952381,"dds":0.6324324324324324,"last_synced_commit":"d4f974c2a4a50eb799160eed68c61063c6c788b5"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrackableEntities%2FEntityFrameworkCore.Scaffolding.Handlebars","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrackableEntities%2FEntityFrameworkCore.Scaffolding.Handlebars/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrackableEntities%2FEntityFrameworkCore.Scaffolding.Handlebars/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TrackableEntities%2FEntityFrameworkCore.Scaffolding.Handlebars/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TrackableEntities","download_url":"https://codeload.github.com/TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254219373,"owners_count":22034397,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-28T17:13:03.588Z","updated_at":"2026-04-21T08:07:21.918Z","avatar_url":"https://github.com/TrackableEntities.png","language":"C#","readme":"# Entity Framework Core Scaffolding with Handlebars\n\n_Scaffold EF Core models using Handlebars templates._\n\n- Uses [Handlebars.NET](https://github.com/rexm/Handlebars.Net) to compile [Handlebars](http://handlebarsjs.com) templates when generating models with the [Entity Framework Core](https://github.com/aspnet/EntityFrameworkCore) scaffolding tools.\n \n## EF Core Community Standup\n\nView the [EF Core Community Standup](https://youtu.be/6Ux7EpgiWXE) episode featuring this framework for scaffolding entities with Handlebars templates. The demos for the episode can be found on this GitHub [repo](https://github.com/TrackableEntities/ef-core-community-handlebars).\n\n## Contributing\n\nBefore creating a pull request, please refer to the [Contributing Guidelines](https://github.com/TrackableEntities/EntityFrameworkCore.Scaffolding.Handlebars/blob/master/.github/CONTRIBUTING.md).\n\n## Prerequisites\n\n- [Visual Studio 2022](https://www.visualstudio.com/downloads/) or greater, [JetBrains Rider](https://www.jetbrains.com/rider) 2024.3 or greater.\n- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet) or greater.\n- [EF Core CLI 10.0](https://docs.microsoft.com/en-us/ef/core/cli/dotnet) or greater.\n  - Install global `dotnet-ef` tool.\n    ```\n    dotnet tool install --global dotnet-ef\n    ```\n  - Or update global `dotnet-ef` tool.\n    ```\n    dotnet tool update --global dotnet-ef\n    ```\n\n## Windows Intel Setup\n\n- Use SQL Server Management Studio to connect to SQL Server\n- The easiest is to use **LocalDb**, which is installed with Visual Studio.\n- Connect to: `(localdb)\\MsSqlLocalDb`.\n- Create a new database named **NorthwindSlim**.\n- Download the `NorthwindSlim.sql` file from \u003chttps://github.com/TrackableEntities/northwind-slim\u003e.\n- Unzip **NorthwindSlim.sql** and run the script to create tables and populate them with data.\n\n## MacOS arm64 Setup (M Series)\n\n- Install [Docker Desktop](https://www.docker.com/products/docker-desktop/)\n- Run Docker SQL Server instance for arm64\n\n```\ndocker run -e \"ACCEPT_EULA=1\" -e \"MSSQL_SA_PASSWORD=MyPass@word\" -e \"MSSQL_PID=Developer\" -e \"MSSQL_USER=SA\" -p 1433:1433 -d --name=sql mcr.microsoft.com/azure-sql-edge\n```\n\n- Add VS Code [Extension for SQL Server](https://marketplace.visualstudio.com/items?itemName=ms-mssql.mssql)\n   - Connect with username `sa` and password `MyPass@word`\n   - Enable trust server certificate when prompted\n   - See [here](https://learn.microsoft.com/en-us/sql/tools/visual-studio-code/sql-server-develop-use-vscode?view=sql-server-ver16) for help connecting and writing commands and queries\n- Create a new database named **NorthwindSlim**.\n- Download the `NorthwindSlim.sql` file from \u003chttps://github.com/TrackableEntities/northwind-slim\u003e.\n- Unzip **NorthwindSlim.sql** and run the script to create tables and populate them with data.\n\n## Upgrading\n\n1. Upgrade `TargetFramework` in **.csproj** file to `net10.0`.\n   - Optional: Set `ImplicitUsings` to `enable`.\n   - Optional: Set `Nullable` to `enable`.\n2. Update the following NuGet packages to `10.0.0` or later:\n   - Microsoft.EntityFrameworkCore.Design\n   - Microsoft.EntityFrameworkCore.SqlServer\n   - EntityFrameworkCore.Scaffolding.Handlebars\n3. Remove the `EnableNullableReferenceTypes` option from `services.AddHandlebarsScaffolding` in `ScaffoldingDesignTimeServices.ConfigureDesignTimeServices`.\n   - Version 6 or greater relies on [support for nullable reference types in EF Core 6](https://docs.microsoft.com/en-us/ef/core/miscellaneous/nullable-reference-types).\n4. Run `dotnet ef dbcontext scaffold` command to regenerate entities.\n   - You may retain your customized Handlebars templates.\n   - [Many-to-many relationships](https://docs.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#many-to-many) will be materialized without the need for an intermediate entity.\n\n## Usage\n\n1. Create a new **.NET 10** class library.\n\n2. Add EF Core SQL Server and Tools NuGet packages.\n    - `Microsoft.EntityFrameworkCore.SqlServer`\n    - `Microsoft.EntityFrameworkCore.Design`\n\n    ```\n    dotnet add package Microsoft.EntityFrameworkCore.SqlServer\n    dotnet add package Microsoft.EntityFrameworkCore.Design\n    ```\n\n3. Add the **EntityFrameworkCore.Scaffolding.Handlebars** NuGet package:\n    - `EntityFrameworkCore.Scaffolding.Handlebars`\n\n    ```\n    dotnet add package EntityFrameworkCore.Scaffolding.Handlebars\n    ```\n\n4. Remove Class1.cs and add a **ScaffoldingDesignTimeServices** class.\n    - Implement `IDesignTimeServices` by adding a `ConfigureDesignTimeServices` method\n      that calls `services.AddHandlebarsScaffolding`.\n    - You can optionally pass a `ReverseEngineerOptions` enum to indicate if you wish \n      to generate only entity types, only a DbContext class, or both (which is the default).\n\n    ```csharp\n    public class ScaffoldingDesignTimeServices : IDesignTimeServices\n    {\n        public void ConfigureDesignTimeServices(IServiceCollection services)\n        {\n            services.AddHandlebarsScaffolding();\n        }\n    }\n    ```\n\n5. Open a command prompt at the project level and use the `dotnet ef` tool to reverse engineer a context and models from an existing database.\n    - Get help on _dotnet-ef-dbcontext-scaffold_ at the command line: `dotnet ef dbcontext scaffold -h`\n    - Execute the following command to reverse engineer classes from the NorthwindSlim database:\n\n    ```\n    dotnet ef dbcontext scaffold \"Data Source=(localdb)\\MSSQLLocalDB; Initial Catalog=NorthwindSlim; Integrated Security=True\" Microsoft.EntityFrameworkCore.SqlServer -o Models -c NorthwindSlimContext -f --context-dir Contexts\n    ```\n\n    - You should see context and/or entity classes appear in the _Models_ folder of the project.\n    - You will also see a **CodeTemplates** folder appear containing Handlebars templates for customizing generation of context and entity type classes.\n    - Add `-d` to the command to use data annotations. You will need to add the **System.ComponentModel.Annotations** package to a .NET Standard library containing linked entity classes.\n\n6. You may _edit_ any of the template files which appear under the **CodeTemplates** folder.\n    - For now you can just add some comments, but you may wish to customize the templates in other ways, for example, by inheriting entities from a base class or implementing\n    specific interfaces.\n    - When you run the _dotnet-ef-dbcontext-scaffold_ command again, you will see your updated reflected in the generated classes.\n\n## Nullable Reference Types\n\nTake advantage of C# nullable reference types by enabling them in your .csproj file. (This is by default in .NET 6 or greater.)\n\n```xml\n\u003cPropertyGroup\u003e\n  \u003cTargetFramework\u003enet6.0\u003c/TargetFramework\u003e\n  \u003cNullable\u003eenable\u003c/Nullable\u003e\n\u003c/PropertyGroup\u003e\n```\n\nNon-nullable properties will include the [null forgiving operator](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving).\n\n```csharp\npublic partial class Product\n{\n    public string ProductName { get; set; } = null!;\n    public decimal? UnitPrice { get; set; }\n}\n```\n\n## Excluded Tables\n\nYou can optionally exclude certain tables from code generation. These may also be qualified by schema name.\n\n```csharp\nservices.AddHandlebarsScaffolding(options =\u003e\n{\n    // Exclude some tables\n    options.ExcludedTables = new List\u003cstring\u003e { \"dbo.Territory\" };\n});\n```\n\n## Custom Template Data\n\nYou may find it useful to add your own custom template data for use in your Handlebars templates. For example, the model namespace is not included by default in the `DbContext` class import statements. To compensate you may wish to add a `models-namespace` template to the **DbImports.hbs** template file.\n\n```hbs\nusing System;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.EntityFrameworkCore.Metadata; // Comment\nusing {{models-namespace}};\n```\n\nLikewise you may wish to specify the name of a model base class in the same way.\n\n```hbs\npublic partial class {{class}} : {{base-class}}\n{\n    {{{\u003e constructor}}}\n    {{\u003e properties}}\n}\n```\n\nYou can then set the value of these templates in the `TemplateData` property of `HandlebarsScaffoldingOptions`.\n\n```csharp\nservices.AddHandlebarsScaffolding(options =\u003e\n{\n    // Add custom template data\n    options.TemplateData = new Dictionary\u003cstring, object\u003e\n    {\n        { \"models-namespace\", \"ScaffoldingSample.Models\" },\n        { \"base-class\", \"EntityBase\" }\n    };\n});\n```\n\n## Schema Folders\n\nYou can generate models in different folders by database schema.\n\n```csharp\nservices.AddHandlebarsScaffolding(options =\u003e\n{\n    // Put Models into folders by DB Schema\n    options.EnableSchemaFolders = true;\n});\n```\n\n## Embedded Templates\n\nHandlebars templates may be embdedded in a separate .NET Standard project that can be shared among multiple .NET Core scaffolding projects. Simply copy the **CodeTemplates** folder to the .NET Standard project and edit the .csproj file to embed them as a resource in the assembly.\n\n```xml\n\u003cItemGroup\u003e\n  \u003cEmbeddedResource Include=\"CodeTemplates\\**\\*.hbs\" /\u003e\n\u003c/ItemGroup\u003e\n```\n\nThen reference the .NET Standard project from the .NET Core projects and specify the templates assembly when adding Handlebars scaffolding in the `ScaffoldingDesignTimeServices` class.\n\n```csharp\npublic class ScaffoldingDesignTimeServices : IDesignTimeServices\n{\n    public void ConfigureDesignTimeServices(IServiceCollection services)\n    {\n        // Get templates assembly\n        var templatesAssembly = Assembly.Load(\"TemplatesAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\");\n\n        // Add Handlebars scaffolding using embedded templates templates\n        services.AddHandlebarsScaffolding(options =\u003e options.EmbeddedTemplatesAssembly = templatesAssembly);\n    }\n}\n```\n\n## Handlebars Helpers and Transformers\n\nYou can register Handlebars helpers in the `ScaffoldingDesignTimeServices` where setup takes place.\n- Create a named tuple as shown with `myHelper` below.\n- The `context` parameter of the helper method provides model data injected by the Handlebars scaffolding extension.\n- Pass the tuple to the `AddHandlebarsHelpers` extension method.\n- To use Handlebars helper defined above, add the following to any of the .hbs files within the CodeTemplates folder: `{{my-helper}}`\n- You may register as many helpers as you wish.\n\nYou can pass transform functions to `AddHandlebarsTransformers` in order to customize generation of entity type definitions, including class names, constructors and properties.\n\n```csharp\npublic class ScaffoldingDesignTimeServices : IDesignTimeServices\n{\n    public void ConfigureDesignTimeServices(IServiceCollection services)\n    {\n        // Add Handlebars scaffolding templates\n        services.AddHandlebarsScaffolding(options =\u003e\n        {\n            // Generate both context and entities\n            options.ReverseEngineerOptions = ReverseEngineerOptions.DbContextAndEntities;\n\n            // Enable Nullable reference types\n            options.EnableNullableReferenceTypes = true;\n\n            // Put Models into folders by DB Schema\n            //options.EnableSchemaFolders = true;\n\n            // Exclude some tables\n            options.ExcludedTables = new List\u003cstring\u003e { \"Territory\", \"EmployeeTerritories\" };\n\n            // Add custom template data\n            options.TemplateData = new Dictionary\u003cstring, object\u003e\n            {\n                { \"models-namespace\", \"ScaffoldingSample.Models\" },\n                { \"base-class\", \"EntityBase\" }\n            };\n        });\n\n        // Register Handlebars helper\n        var myHelper = (helperName: \"my-helper\", helperFunction: (Action\u003cTextWriter, Dictionary\u003cstring, object\u003e, object[]\u003e) MyHbsHelper);\n\n        // Add optional Handlebars helpers\n        services.AddHandlebarsHelpers(myHelper);\n\n            // Add Handlebars transformer for Country property\n            services.AddHandlebarsTransformers(\n                propertyTransformer: p =\u003e\n                    p.PropertyName == \"Country\"\n                        ? new EntityPropertyInfo(\"Country?\", p.PropertyName, false)\n                        : new EntityPropertyInfo(p.PropertyType, p.PropertyName, p.PropertyIsNullable));\n\n            // Add Handlebars transformer for Id property\n            //services.AddHandlebarsTransformers2(\n            //    propertyTransformer: (e, p) =\u003e\n            //        $\"{e.Name}Id\" == p.PropertyName\n            //            ? new EntityPropertyInfo(p.PropertyType, \"Id\", false)\n            //            : new EntityPropertyInfo(p.PropertyType, p.PropertyName, p.PropertyIsNullable));\n\n            // Add optional Handlebars transformers\n            //services.AddHandlebarsTransformers2(\n            //    entityTypeNameTransformer: n =\u003e n + \"Foo\",\n            //    entityFileNameTransformer: n =\u003e n + \"Foo\",\n            //    constructorTransformer: (e, p) =\u003e new EntityPropertyInfo(p.PropertyType + \"Foo\", p.PropertyName + \"Foo\"),\n            //    propertyTransformer: (e, p) =\u003e new EntityPropertyInfo(p.PropertyType, p.PropertyName + \"Foo\"),\n            //    navPropertyTransformer: (e, p) =\u003e new EntityPropertyInfo(p.PropertyType + \"Foo\", p.PropertyName + \"Foo\"));\n    }\n\n    // Sample Handlebars helper\n    void MyHbsHelper(TextWriter writer, Dictionary\u003cstring, object\u003e context, object[] parameters)\n    {\n        writer.Write(\"// My Handlebars Helper\");\n    }\n}\n```\n\n## Extending the OnModelCreating Method\n\nThere are times when you might like to modify generated code, for example, by adding a `HasConversion` method to an entity property in the `OnModelCreating` method of the generated class that extends `DbContext`. However, doing so may prove futile because added code would be overwritten the next time you run the `dotnet ef dbcontext scaffold` command.\n- Rather than modifying generated code, a better idea would be to extend it by using _partial classes and methods_. To enable this scenario, the generated `DbContext` class is already defined using the `partial` keyword, and it contains a partial `OnModelCreatingPartial` method that is invoked at the end of the `OnModelCreating` method.\n- To implement the partial method, simply add a new class to your project with the same name as the generated `DbContext` class, and define it as `partial`. Then add a `OnModelCreatingPartial` method with the same signature as the partial method defined in the generated `DbContext` class.\n\n```csharp\n// Place in separate class file (NorthwindSlimContextPartial.cs)\npublic partial class NorthwindSlimContext\n{\n    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)\n    {\n        modelBuilder.Entity\u003cEmployee\u003e()\n            .Property(e =\u003e e.Country)\n            .HasConversion(\n                v =\u003e v.ToString(),\n                v =\u003e (Country)Enum.Parse(typeof(Country), v));\n\n        modelBuilder.Entity\u003cCustomer\u003e()\n            .Property(e =\u003e e.Country)\n            .HasConversion(\n                v =\u003e v.ToString(),\n                v =\u003e (Country)Enum.Parse(typeof(Country), v));\n    }\n}\n```\n\n## Generating TypeScript Entities\n\nTo generate TypeScript entities simply pass `LanguageOptions.TypeScript` to `AddHandlebarsScaffolding`. Since generating a `DbContext` class is strictly a server-side concern, you should also pass `ReverseEngineerOptions.EntitiesOnly` to `AddHandlebarsScaffolding`.\n\n```csharp\npublic class ScaffoldingDesignTimeServices : IDesignTimeServices\n{\n    public void ConfigureDesignTimeServices(IServiceCollection services)\n    {\n        // Generate entities only\n        var options = ReverseEngineerOptions.EntitiesOnly;\n\n        // Generate TypeScript files\n        var language = LanguageOptions.TypeScript;\n\n        // Add Handlebars scaffolding templates\n        services.AddHandlebarsScaffolding(options, language);\n    }\n}\n```\n\n## Taking Full Control by Extending Handlebars Generators\n\n\u003e For an example of this approach, see `MyHbsCSharpEntityTypeGenerator` in the [ef-core-community-handlebars](https://github.com/TrackableEntities/ef-core-community-handlebars/blob/master/ScaffoldingHandlebars.Tooling/MyHbsCSharpEntityTypeGenerator.cs) repo.\n\nTo take full control of context and entity generation, you can extend `HbsCSharpDbContextGenerator` and `HbsCSharpEntityTypeGenerator`, overriding select virtual methods. Then register your custom generators in `ScaffoldingDesignTimeServices.ConfigureDesignTimeServices`.\n\nFor example, you may want to add `property-isprimarykey` to the template data in order to insert some code or a comment.\n\n1. Add a `MyHbsCSharpEntityTypeGenerator` to the **.Tooling** project.\n   - Extend `HbsCSharpEntityTypeGenerator`.\n   - Override `GenerateProperties`.\n   - Copy code from the base `GenerateProperties` method.\n   - Add code that inserts `property-isprimarykey` into the template data.\n   ```csharp\n   protected override void GenerateProperties(IEntityType entityType)\n   {\n      var properties = new List\u003cDictionary\u003cstring, object\u003e\u003e();\n      foreach (var property in entityType.GetProperties().OrderBy(p =\u003e p.GetColumnOrdinal()))\n      {\n         // Code elided for clarity\n         properties.Add(new Dictionary\u003cstring, object\u003e\n         {\n               { \"property-type\", propertyType },\n               { \"property-name\", property.Name },\n               { \"property-annotations\",  PropertyAnnotationsData },\n               { \"property-comment\", property.GetComment() },\n               { \"property-isnullable\", property.IsNullable },\n               { \"nullable-reference-types\", _options?.Value?.EnableNullableReferenceTypes == true },\n\n               // Add new item to template data\n               { \"property-isprimarykey\", property.IsPrimaryKey() }\n         });\n      }\n\n      var transformedProperties = EntityTypeTransformationService.TransformProperties(properties);\n\n      // Add to transformed properties\n      for (int i = 0; i \u003c transformedProperties.Count ; i++)\n      {\n         transformedProperties[i].Add(\"property-isprimarykey\", properties[i][\"property-isprimarykey\"]);\n      }\n\n      TemplateData.Add(\"properties\", transformedProperties);\n   }\n   ```\n2. Register `MyHbsCSharpEntityTypeGenerator` in `ScaffoldingDesignTimeServices.ConfigureDesignTimeServices`.\n   ```csharp\n   services.AddSingleton\u003cICSharpEntityTypeGenerator, MyHbsCSharpEntityTypeGenerator\u003e();\n   ```\n3. Update **CSharpEntityType/Partials/Properties.hbs** to add `property-isprimarykey`.\n   ```handlebars\n   {{#if property-isprimarykey}} // Primary Key{{/if}}\n   ```\n4. Run the `dotnet ef dbcontext scaffold` command from above.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrackableentities%2Fentityframeworkcore.scaffolding.handlebars","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrackableentities%2Fentityframeworkcore.scaffolding.handlebars","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrackableentities%2Fentityframeworkcore.scaffolding.handlebars/lists"}