Skip to content
Sample using T4 with reverse engineering in EF Core
C# TSQL
Branch: master
Clone or download

Latest commit

Files

Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
ChinookApp Use ErikEJ.EntityFrameworkCore.SqlServer.Dacpac Feb 4, 2020
ChinookDatabase
EFCore.TextTemplating Use ErikEJ.EntityFrameworkCore.SqlServer.Dacpac Feb 4, 2020
.gitattributes
.gitignore Add a license Feb 1, 2020
EFCore.TextTemplating.sln
LICENSE Add a license Feb 1, 2020
README.md

README.md

EFCore.TextTemplating

This sample shows how to use T4 templates to scaffold code when reverse engineering a model from a database using EF Core.

This repo contains three projects:

Project Description
ChinookApp An example app containing the scaffolded DbContext type, configuration, and entity types
ChinookDatabase The database project for our example app
EFCore.TextTemplating Contains the T4 templates and required plumbing to hook them into EF Core.
Copy into your solution to get started

ChinookApp references EFCore.TextTemplating and contains an assembly-level attribute that EF Core uses to discover its design-time services. Your app needs to do this too:

[assembly: DesignTimeServicesReference(
    "EFCore.TextTemplating.DesignTimeServices, EFCore.TextTemplating")]

ChinookApp also references the EF Core tools and my pluralizer extension to enhance the scaffolded code and ErikEJ.EntityFrameworkCore.SqlServer.Dacpac from the EF Core Power Tools to reverese engineer directly from a DACPAC.

EFCore.TextTemplating contains three templates: one for scaffolding the DbContext, one for the IEntityTypeConfiguration implementations, and one for the entity types.

The templates in this repo are merely a starting point. Feel free to tweak them to your heart's content. The resulting code is a little ugly, but I refrained from adding formatting code to keep the templates as simple as possible. I scaffold the code I would want to use, so only the parts of the model that actually affect EF Core behavior are scaffolded. Things like sequences, constraint names, and non-unique indexes are ignored.

Warning! Staring directly at a T4 template without syntax highlighting may hurt your eyes. I recommend using the Devart T4 Editor for Visual Studio or T4 Support by Zachary Becknell for VS Code.

If you're using Visual Studio, merely saving the template files is enough.

If you're using VS Code, you'll need to use dotnet-t4 after editing the files:

dotnet tool install -g dotnet-t4
t4 MyDbContextGenerator.tt -c MyDbContextGenerator -o MyDbContextGenerator.cs

Reverse engineering the model can be done in the normal way:

dotnet ef dbcontext scaffold \
    ../ChinookDatabase/bin/Debug/ChinookDatabase.dacpac \
    ErikEJ.EntityFrameworkCore.SqlServer.Dacpac \
    --output-dir Models \
    --context-dir Data \
    --force

or

Scaffold-DbContext `
    ..\ChinookDatabase\bin\Debug\ChinookDatabase.dacpac `
    ErikEJ.EntityFrameworkCore.SqlServer.Dacpac `
    -OutputDir Models `
    -ContextDir Data `
    -Force

API Reference

In addition to all the usual T4 goodness, the following helpers are also available.

Method Description
AnnotationCode.IsHandledByConvention Gets a value indicating whether the annotation would be handled by convention. If so, generating code is unnecessary
AnnotationCode.GenerateFluentApi Generates provider-specific fluent API for the given annotation (like .IsClustered())
Code.Fragment Generates code from fragments returned by the ProviderCode and AnnotationCode helpers
Code.Identifier Generates a valid identifier unique within the given scope
Code.Lambda Generates a property access lambda
Code.Literal Generates a literal for given value
Code.Namespace Generates a valid namespace from its given parts
Code.Reference Generates a C# reference to the given type
Code.UnknownLiteral Generates a literal for a value whose type isn't known at compile time
ProviderCode.GenerateUseProvide Generates the DbContextOptions code to configure the provider (like .UseSqlServer("Data Source=..."))

For example:

Input

builder.HasKey(<#= Code.Lambda(primaryKey.Properties) #>);

Output

builder.HasKey(x => x.Id);
You can’t perform that action at this time.