Condividi tramite


Modelli di reverse engineering personalizzati

Nota

Questa funzionalità è stata aggiunta in EF Core 7.

Durante l'ingegneria inversa, Entity Framework Core si impegna a eseguire lo scaffolding di codice valido e generico che può essere usato in un'ampia gamma di tipi di app e usa convenzioni di codifica comuni per un aspetto coerente e un aspetto familiare. In alcuni casi, tuttavia, sono consigliabili codice più specializzato e stili di codifica alternativi. Questo articolo illustra come personalizzare il codice con scaffolding usando modelli di testo T4.

Prerequisiti

Questo articolo presuppone che si abbia familiarità con il reverse engineering in EF Core. In caso contrario, leggere l'articolo prima di procedere.

Aggiunta dei modelli predefiniti

Il primo passaggio per personalizzare il codice con scaffolding consiste nell'aggiungere i modelli predefiniti al progetto. I modelli predefiniti sono quelli usati internamente da EF Core durante l'ingegneria inversa. Forniscono un punto di partenza per iniziare a personalizzare il codice con scaffolding.

Per iniziare, installare il pacchetto del modello di EF Core per dotnet new:

dotnet new install Microsoft.EntityFrameworkCore.Templates

È ora possibile aggiungere i modelli predefiniti al progetto. Eseguire questa operazione eseguendo il comando seguente dalla directory del progetto.

dotnet new ef-templates

Questo comando aggiunge i file seguenti al progetto.

  • CodeTemplates/
    • EFCore/
      • DbContext.t4
      • EntityType.t4

Il DbContext.t4 modello viene usato per eseguire lo scaffolding di una classe DbContext per il database e il modello viene usato per eseguire lo EntityType.t4 scaffolding delle classi dei tipi di entità per ogni tabella e vista nel database.

Suggerimento

L'estensione .t4 viene usata (invece di .tt) per impedire a Visual Studio di trasformare i modelli. I modelli verranno invece trasformati da EF Core.

Introduzione a T4

Aprire il modello ed esaminarne il DbContext.t4 contenuto. Questo file è un modello di testo T4. T4 è una lingua per la generazione di testo tramite .NET. Il codice seguente è solo a scopo illustrativo; non rappresenta il contenuto completo del file.

Importante

I modelli di testo T4, in particolare quelli che generano codice, possono essere difficili da leggere senza evidenziazione della sintassi. Se necessario, cercare un'estensione nell'editor di codice che abilita l'evidenziazione della sintassi 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 #>;

Le prime righe che iniziano con <#@ sono denominate direttive. Influiscono sulla modalità di trasformazione del modello. La tabella seguente descrive brevemente ogni tipo di direttiva utilizzata.

Direttiva Descrizione
template Specifica hostSpecific="true" che consente di usare la Host proprietà all'interno del modello per accedere ai servizi di EF Core.
assembly Aggiunge i riferimenti all'assembly necessari per compilare il modello.
parameter Dichiara i parametri che verranno passati da EF Core durante la trasformazione del modello.
import Analogamente alle direttive C# using, porta gli spazi dei nomi nell'ambito del codice del modello.

Dopo le direttive, la sezione successiva di viene chiamata blocco di DbContext.t4 controllo. Un blocco di controllo standard inizia con e termina con <##>. Il codice all'interno verrà eseguito durante la trasformazione del modello. Per un elenco di proprietà e metodi disponibili all'interno dei blocchi di controllo, vedere la classe TextTransformation .

Qualsiasi elemento all'esterno di un blocco di controllo verrà copiato direttamente nell'output del modello.

Un blocco di controllo delle espressioni inizia con <#=. Il codice all'interno verrà valutato e il risultato verrà aggiunto all'output del modello. Questi argomenti sono simili agli argomenti stringa interpolati C#.

Per una spiegazione più dettagliata e completa della sintassi T4, vedere Scrittura di un modello di testo T4.

Personalizzare i tipi di entità

Verrà ora illustrato come personalizzare un modello. Per impostazione predefinita, EF Core genera il codice seguente per le proprietà di navigazione della raccolta.

public virtual ICollection<Album> Albums { get; } = new List<Album>();

L'uso List<T> è un'impostazione predefinita valida per la maggior parte delle applicazioni. Tuttavia, se usi un framework basato su XAML come WPF, WinUI o .NET MAUI, spesso vuoi usare ObservableCollection<T> per abilitare il data binding.

Aprire il EntityType.t4 modello e trovare dove genera List<T>. Avrà l'aspetto seguente:

    if (navigation.IsCollection)
    {
#>
    public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new List<<#= targetType #>>();
<#
    }

Sostituire List con ObservableCollection.

public virtual ICollection<<#= targetType #>> <#= navigation.Name #> { get; } = new ObservableCollection<<#= targetType #>>();

È anche necessario aggiungere una using direttiva al codice con scaffolding. Gli using vengono specificati in un elenco nella parte superiore del modello. Aggiungere System.Collections.ObjectModel all'elenco.

var usings = new List<string>
{
    "System",
    "System.Collections.Generic",
    "System.Collections.ObjectModel"
};

Testare le modifiche usando i comandi di reverse engineering. I modelli all'interno del progetto vengono usati automaticamente dai comandi.

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer

Se il comando è stato eseguito in precedenza, aggiungere l'opzione --force per sovrascrivere i file esistenti.

Se tutto è stato eseguito correttamente, le proprietà di navigazione della raccolta dovrebbero ora usare ObservableCollection<T>.

public virtual ICollection<Album> Albums { get; } = new ObservableCollection<Album>();

Aggiornamento dei modelli

Quando si aggiungono i modelli predefiniti al progetto, viene creata una copia di tali modelli in base a tale versione di EF Core. Man mano che i bug vengono corretti e le funzionalità vengono aggiunte nelle versioni successive di EF Core, i modelli potrebbero non essere aggiornati. È consigliabile esaminare le modifiche apportate nei modelli di EF Core e unirle nei modelli personalizzati.

Un modo per esaminare le modifiche apportate ai modelli di EF Core consiste nell'usare Git per confrontarle tra le versioni. Il comando seguente clonerà il repository EF Core e genererà un diff di questi file tra le versioni 7.0.0 e 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

Un altro modo per esaminare le modifiche consiste nel scaricare le due versioni di Microsoft.EntityFrameworkCore.Templates da NuGet, estrarre il relativo contenuto (è possibile modificare le estensioni di file in .zip) e confrontare tali file.

Prima di aggiungere i modelli predefiniti a un nuovo progetto, ricordarsi di eseguire l'aggiornamento al pacchetto di modelli di EF Core più recente.

dotnet new update

Uso avanzato

Ignorare il modello di input

I Model parametri e EntityType rappresentano un modo possibile per eseguire il mapping al database. È possibile scegliere di ignorare o modificare parti del modello. Ad esempio, i nomi di navigazione forniti potrebbero non essere ideali ed è possibile sostituirli con i propri durante lo scaffolding del codice. Altri elementi, ad esempio i nomi dei vincoli e i filtri di indice, vengono usati solo dalle migrazioni e possono essere omessi dal modello se non si intende usare Migrazioni con il codice con scaffolding. Analogamente, potresti voler omettere sequenze o vincoli predefiniti se non vengono usati dall'app.

Quando si apportano modifiche avanzate come questa, assicurarsi che il modello risultante rimanga compatibile con il database. Esaminare il codice SQL generato da dbContext.Database.GenerateCreateScript() è un buon modo per convalidarlo.

Classi di configurazione delle entità

Per i modelli di grandi dimensioni, il metodo OnModelCreating della classe DbContext può diventare ingestibile. Un modo per risolvere questo problema consiste nell'usare IEntityTypeConfiguration<T> le classi. Per altre informazioni su queste classi, vedere Creazione e configurazione di un modello .

Per eseguire lo scaffolding di queste classi, è possibile usare un terzo modello denominato EntityTypeConfiguration.t4. Analogamente al EntityType.t4 modello, viene usato per ogni tipo di entità nel modello e usa il parametro del EntityType modello.

Scaffolding di altri tipi di file

Lo scopo principale dell'ingegneria inversa in EF Core è eseguire lo scaffolding di un dbContext e dei tipi di entità. Tuttavia, non c'è nulla negli strumenti che richiedono di eseguire effettivamente lo scaffolding del codice. Ad esempio, è possibile eseguire invece lo scaffolding di un diagramma di relazione di entità usando 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 #>
<#
        }
    }
#>
```