Vlastní šablony zpětné analýzy
Poznámka
Tato funkce byla přidána v EF Core 7.
I když se Entity Framework Core snaží generovat dobrý, obecný kód, který lze použít v různých typech aplikací a používá společné konvence kódování pro konzistentní vzhled a známý pocit. Někdy je však žádoucí specializovanější kód a alternativní styly kódování. Tento článek ukazuje, jak přizpůsobit vygenerovaný kód pomocí textových šablon T4.
Požadavky
Tento článek předpokládá, že znáte zpětnou přípravu v EF Core. Pokud ne, přečtěte si prosím tento článek, než budete pokračovat.
Přidání výchozích šablon
Prvním krokem k přizpůsobení vygenerovaného kódu je přidání výchozích šablon do projektu. Výchozí šablony jsou ty, které EF Core interně používá při zpětné analýze. Poskytují výchozí bod pro zahájení přizpůsobení vygenerovaného kódu.
Začněte instalací balíčku šablony EF Core pro dotnet new
:
dotnet new install Microsoft.EntityFrameworkCore.Templates
Teď můžete do projektu přidat výchozí šablony. Uděláte to spuštěním následujícího příkazu z adresáře projektu.
dotnet new ef-templates
Tento příkaz přidá do projektu následující soubory.
- CodeTemplates/
- EFCore/
- DbContext.t4
- EntityType.t4
- EFCore/
Šablona DbContext.t4
slouží k generování třídy DbContext pro databázi a EntityType.t4
šablona slouží k generování tříd typů entit pro každou tabulku a zobrazení v databázi.
Tip
Rozšíření .t4 se používá (místo .tt), aby se zabránilo transformaci šablon v sadě Visual Studio. Šablony se místo toho transformují pomocí EF Core.
Úvod do T4
Pojďme šablonu otevřít DbContext.t4
a zkontrolovat její obsah. Tento soubor je textová šablona T4. T4 je jazyk pro generování textu pomocí .NET. Následující kód je určen pouze pro ilustrativní účely; nepředstavuje úplný obsah souboru.
Důležité
Textové šablony T4 – zejména ty, které generují kód – můžou být obtížně čitelné bez zvýrazňování syntaxe. V případě potřeby vyhledejte rozšíření editoru kódu, které umožňuje zvýraznění syntaxe T4.
<#@ template hostSpecific="true" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="NamespaceHint" type="System.String" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
<#
if (!string.IsNullOrEmpty(NamespaceHint))
{
#>
namespace <#= NamespaceHint #>;
Prvních pár řádků, které začínají <#@
, se nazývají direktivy. Ovlivňují způsob transformace šablony. Následující tabulka stručně popisuje každý druh použité direktivy.
Direktiva | Popis |
---|---|
template |
Určuje hostSpecific="true", která umožňuje používat Host vlastnost uvnitř šablony pro přístup ke službám EF Core. |
assembly |
Přidá odkazy na sestavení potřebné ke kompilaci šablony. |
parameter |
Deklaruje parametry, které předá EF Core při transformaci šablony. |
import |
Podobně jako direktivy using jazyka C# přenese obory názvů do oboru kódu šablony. |
Po direktivách se další část DbContext.t4
nazývá řídicí blok. Standardní řídicí blok začíná <#
a končí #>
na . Kód uvnitř se spustí při transformaci šablony. Seznam vlastností a metod dostupných uvnitř řídicích bloků naleznete v TextTransformation třídy.
Cokoli mimo řídicí blok se zkopíruje přímo do výstupu šablony.
Blok ovládacího prvku výrazu začíná znakem <#=
. Kód uvnitř se vyhodnotí a výsledek se přidá do výstupu šablony. Jsou podobné interpolovaným řetězcovým argumentům jazyka C#.
Podrobnější a úplné vysvětlení syntaxe T4 najdete v tématu Psaní textové šablony T4.
Přizpůsobení typů entit
Pojďme si projít, jak si šablonu přizpůsobit. Ef Core ve výchozím nastavení generuje následující kód pro vlastnosti navigace v kolekci.
public virtual ICollection<Album> Albums { get; } = new List<Album>();
Použití List<T>
je vhodné výchozí nastavení pro většinu aplikací. Pokud ale používáte architekturu založenou na XAML, jako je WPF, WinUI nebo .NET MAUI, často chcete místo toho povolit ObservableCollection<T>
datovou vazbu.
EntityType.t4
Otevřete šablonu a vyhledejte, kde se List<T>
generuje . Vypadá to takhle:
if (navigation.IsCollection)
{
#>
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new List<<#= targetType #>>();
<#
}
Nahradit seznam observableCollection.
public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new ObservableCollection<<#= targetType #>>();
Musíme také přidat direktivu using
do vygenerovaného kódu. Použití jsou zadána v seznamu v horní části šablony. Přidejte System.Collections.ObjectModel
do seznamu.
var usings = new List<string>
{
"System",
"System.Collections.Generic",
"System.Collections.ObjectModel"
};
Otestujte změny pomocí příkazů zpětné analýzy. Šablony v projektu se automaticky používají příkazy.
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer
Pokud jste příkaz spustili dříve, přidejte --force
možnost přepsání existujících souborů.
Pokud jste všechno udělali správně, měly by se teď používat ObservableCollection<T>
vlastnosti navigace v kolekci .
public virtual ICollection<Album> Albums { get; } = new ObservableCollection<Album>();
Aktualizace šablon
Když do projektu přidáte výchozí šablony, vytvoří jejich kopii na základě této verze EF Core. Protože chyby jsou opravené a funkce se přidávají v dalších verzích EF Core, můžou se vaše šablony stát zastaralými. Měli byste zkontrolovat změny provedené v šablonách EF Core a sloučit je do přizpůsobených šablon.
Jedním ze způsobů, jak zkontrolovat změny provedené v šablonách EF Core, je použití Gitu k porovnání mezi verzemi. Následující příkaz naklonuje úložiště EF Core a vygeneruje rozdíl těchto souborů mezi verzemi 7.0.0 a 8.0.0.
git clone --no-checkout https://github.com/dotnet/efcore.git
cd efcore
git diff v7.0.0 v8.0.0 -- src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt src/EFCore.Design/Scaffolding/Internal/CSharpEntityTypeGenerator.tt
Dalším způsobem, jak zkontrolovat změny, je stáhnout dvě verze Microsoft.EntityFrameworkCore.Templates z NuGetu, extrahovat jejich obsah (můžete změnit přípony souborů na .zip) a porovnat tyto soubory.
Před přidáním výchozích šablon do nového projektu nezapomeňte aktualizovat nejnovější balíček šablon EF Core.
dotnet new update
Pokročilé využití
Ignorování vstupního modelu
EntityType
Parametry Model
představují jeden možný způsob mapování na databázi. Můžete se rozhodnout ignorovat nebo změnit části modelu. Například názvy navigace, které poskytujeme, nemusí být ideální a při generování kódu je můžete nahradit vlastními názvy. Další věci, jako jsou názvy omezení a filtry indexů, se používají jenom migrace a pokud nemáte v úmyslu používat migrace s vygenerovaným kódem, můžete je v modelu bezpečně vynechat. Podobně můžete chtít vynechat sekvence nebo výchozí omezení, pokud je vaše aplikace nepoužívá.
Při provádění pokročilých změn, jako je tato, stačí zajistit, aby výsledný model zůstal kompatibilní s databází. Kontrola SQL vygenerovaného dbContext.Database.GenerateCreateScript()
sql je dobrým způsobem, jak to ověřit.
Třídy konfigurace entit
U velkých modelů může být metoda OnModelCreating třídy DbContext nespravovatelně velká. Jedním ze způsobů, jak to vyřešit, je použití IEntityTypeConfiguration<T>
tříd. Další informace o těchto třídách najdete v tématu Vytvoření a konfigurace modelu .
Pro generování těchto tříd můžete použít třetí šablonu s názvem EntityTypeConfiguration.t4
. Podobně jako šablona EntityType.t4
se používá pro každý typ entity v modelu a používá EntityType
parametr šablony.
Generování dalších typů souborů
Primárním účelem zpětné analýzy v EF Core je generování typů DbContext a entit. V nástrojích ale není nic, co by vyžadovalo skutečné generování kódu. Můžete například místo toho vygenerovat diagram vztahů entit pomocí mermaid.
<#@ output extension=".md" #>
<#@ assembly name="Microsoft.EntityFrameworkCore" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Relational" #>
<#@ assembly name="Microsoft.EntityFrameworkCore.Design" #>
<#@ parameter name="Model" type="Microsoft.EntityFrameworkCore.Metadata.IModel" #>
<#@ parameter name="Options" type="Microsoft.EntityFrameworkCore.Scaffolding.ModelCodeGenerationOptions" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="Microsoft.EntityFrameworkCore" #>
# <#= Options.ContextName #>
```mermaid
erDiagram
<#
foreach (var entityType in Model.GetEntityTypes().Where(e => !e.IsSimpleManyToManyJoinEntityType()))
{
#>
<#= entityType.Name #> {
}
<#
foreach (var foreignKey in entityType.GetForeignKeys())
{
#>
<#= entityType.Name #> <#= foreignKey.IsUnique ? "|" : "}" #>o--<#= foreignKey.IsRequired ? "|" : "o" #>| <#= foreignKey.PrincipalEntityType.Name #> : "<#= foreignKey.GetConstraintName() #>"
<#
}
foreach (var skipNavigation in entityType.GetSkipNavigations().Where(n => n.IsLeftNavigation()))
{
#>
<#= entityType.Name #> }o--o{ <#= skipNavigation.TargetEntityType.Name #> : <#= skipNavigation.JoinEntityType.Name #>
<#
}
}
#>
```