Modelos de Engenharia Reversa Personalizados

Observação

Esse recurso foi adicionado no EF Core 7.

Durante a engenharia reversa, o Entity Framework Core se esforça para scaffolding de um código bom e de uso geral que possa ser usado em uma variedade de tipos de aplicativos e usa convenções de codificação comuns para uma aparência consistente e familiar. Às vezes, porém, é desejável um código mais especializado e estilos de codificação alternativos. Este artigo mostra como personalizar o código de scaffolding usando modelos de texto T4.

Pré-requisitos

Este artigo pressupõe que você esteja familiarizado com a engenharia reversa no EF Core. Caso contrário, revise esse artigo antes de continuar.

Adição dos modelos padrão

A primeira etapa para personalizar o código de scaffolding é adicionar os modelos padrão ao seu projeto. Os modelos padrão são os usados internamente pelo EF Core durante a engenharia reversa. Eles fornecem um ponto de partida para você começar a personalizar o código de scaffolding.

Comece instalando o pacote de modelos EF Core para dotnet new:

dotnet new install Microsoft.EntityFrameworkCore.Templates

Agora você pode adicionar os modelos padrão ao seu projeto. Para isso, execute o seguinte comando no diretório do projeto.

dotnet new ef-templates

Esse comando adiciona os seguintes arquivos ao seu projeto.

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

O modelo DbContext.t4 é usado para scaffolding de uma classe DbContext para o banco de dados, e o modelo EntityType.t4 é usado para scaffolding de classes de tipo de entidade para cada tabela e exibição no banco de dados.

Dica

A extensão .t4 é usada (em vez de .tt) para evitar que o Visual Studio transforme os modelos. Em vez disso, os modelos serão transformados pelo EF Core.

Introdução ao T4

Vamos abrir o modelo DbContext.t4 e inspecionar seu conteúdo. Esse arquivo é um modelo de texto T4. T4 é uma linguagem para gerar texto usando .NET. O código a seguir é apenas para fins ilustrativos; ele não representa o conteúdo completo do arquivo.

Importante

Os modelos de texto T4, especialmente os que geram código, podem ser difíceis de ler sem o realce de sintaxe. Se necessário, procure uma extensão para o editor de código que permita o realce de sintaxe 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 #>;

As primeiras linhas que começam com <#@ são chamadas de diretivas. Elas afetam a forma como o modelo é transformado. A tabela a seguir descreve resumidamente cada tipo de diretiva usada.

Diretiva Descrição
template Especifica hostSpecific="true", o que permite usar a propriedade Host no modelo para acessar os serviços do EF Core.
assembly Adiciona referências de assembly necessárias para compilar o modelo.
parameter Declara os parâmetros que serão passados pelo EF Core ao transformar o modelo.
import Como o C# usando diretivas, coloca namespaces no escopo do código do modelo.

Após as diretivas, a próxima seção de DbContext.t4 é chamada de bloco de controle. Um bloco de controle padrão começa com <# e termina com #>. O código dentro dele será executado ao transformar o modelo. Para obter uma lista de propriedades e métodos disponíveis nos blocos de controle, confira a classe TextTransformation.

Qualquer coisa fora de um bloco de controle será copiada diretamente para a saída do modelo.

Um bloco de controle de expressão começa com <#=. O código dentro dele será avaliado e o resultado será adicionado à saída do modelo. Eles são semelhantes aos argumentos de cadeia de caracteres interpolados em C#.

Para obter uma explicação mais detalhada e completa da sintaxe do T4, confira Escrever um modelo de texto T4.

Personalizar os tipos de entidades

Veja como é a personalização de um modelo. Por padrão, o EF Core gera o seguinte código para as propriedades de navegação da coleção.

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

O uso de List<T> é um bom padrão para a maioria dos aplicativos. No entanto, se você estiver usando uma estrutura baseada em XAML, como WPF, WinUI ou .NET MAUI, geralmente quer usar ObservableCollection<T> para habilitar a associação de dados.

Abra o modelo EntityType.t4 e localize onde ele gera List<T>. Ela tem esta aparência:

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

Substitua List por ObservableCollection.

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

Também precisamos adicionar uma diretiva using ao código de scaffolding. Os usos são especificados em uma lista próxima à parte superior do modelo. Adicione System.Collections.ObjectModel à lista.

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

Teste as alterações usando os comandos de engenharia reversa. Os modelos em seu projeto são usados automaticamente pelos comandos.

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

Se você já executou o comando anteriormente, adicione a opção --force para substituir os arquivos existentes.

Se você fez tudo corretamente, as propriedades de navegação da coleção agora devem usar ObservableCollection<T>.

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

Atualizar modelos

Ao adicionar os modelos padrão ao projeto, ele cria uma cópia deles com base nessa versão do EF Core. À medida que os bugs são corrigidos e os recursos são adicionados nas versões seguintes do EF Core, seus modelos podem ficar desatualizados. Você deve examinar as alterações feitas nos modelos do EF Core e mesclá-las aos seus modelos personalizados.

Uma maneira de examinar as alterações feitas nos modelos do EF Core é usar o git para compará-las entre as versões. O comando a seguir clonará o repositório do EF Core e gerará uma comparação desses arquivos entre as versões 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

Outra maneira de examinar as alterações é baixar as duas versões do Microsoft.EntityFrameworkCore.Templates do NuGet, extrair seu conteúdo (você pode alterar as extensões de arquivo para .zip) e comparar esses arquivos.

Antes de adicionar os modelos padrão a um novo projeto, lembre-se de atualizar para o pacote de modelos mais recente do EF Core.

dotnet new update

Uso avançado

Ignorar o modelo de entrada

Os parâmetros Model e EntityType representam uma forma possível de mapeamento para o banco de dados. Você pode optar por ignorar ou alterar partes do modelo. Por exemplo, os nomes de navegação que fornecemos podem não ser ideais, e você pode substituí-los por seus próprios ao fazer scaffolding do código. Outros itens, como nomes de restrições e filtros de índices, são usados apenas pelas migrações e podem ser omitidos com segurança do modelo se você não pretende usar as migrações com o código de scaffolding. Da mesma forma, talvez você queira omitir sequências ou restrições padrão se elas não forem usadas pelo seu aplicativo.

Ao fazer alterações avançadas como essa, verifique se o modelo resultante permanece compatível com o banco de dados. A revisão do SQL gerado pelo dbContext.Database.GenerateCreateScript() é uma boa maneira de validar isso.

Classes de configuração da entidade

Para modelos grandes, o método OnModelCreating da classe DbContext pode se tornar excessivamente grande. Uma maneira de resolver isso é usar classes IEntityTypeConfiguration<T>. Confira Criação e configuração de um modelo para obter mais informações sobre essas classes.

Para scaffolding dessas classes, você pode usar um terceiro modelo chamado EntityTypeConfiguration.t4. Como o modelo EntityType.t4, ele é usado para cada tipo de entidade no modelo e usa o parâmetro do modelo EntityType.

Scaffolding de outros tipos de arquivos

O objetivo principal da engenharia reversa no EF Core é estruturar um DbContext e tipos de entidades. No entanto, não há nada nas ferramentas que exija que você realmente faça scaffolding de código. Por exemplo, em vez disso, você pode fazer scaffolding de um diagrama de relação de entidade usando o 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 #>
<#
        }
    }
#>
```