Пользовательские шаблоны обратной инженерии

Примечание.

Эта функция была добавлена в EF Core 7.

В то время как обратная инженерия Entity Framework Core стремится создать хороший код общего назначения, который можно использовать в различных типах приложений и использует общие соглашения по программированию для согласованного внешнего вида и знакомого интерфейса. Однако иногда желательно использовать более специализированный код и альтернативные стили программирования. В этой статье показано, как настроить шаблонный код с помощью текстовых шаблонов T4.

Необходимые компоненты

В этой статье предполагается, что вы знакомы с обратным проектированием в EF Core. Если нет, ознакомьтесь с этой статьей, прежде чем продолжить.

Добавление шаблонов по умолчанию

Первым шагом в настройке шаблонного кода является добавление шаблонов по умолчанию в проект. Шаблоны по умолчанию используются внутри EF Core при обратном проектировании. Они предоставляют отправную точку для начала настройки шаблонного кода.

Начните с установки пакета шаблона EF Core для dotnet new:

dotnet new install Microsoft.EntityFrameworkCore.Templates

Теперь вы можете добавить шаблоны по умолчанию в проект. Для этого выполните следующую команду из каталога проекта.

dotnet new ef-templates

Эта команда добавляет в проект следующие файлы.

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

Шаблон DbContext.t4 используется для формирования класса DbContext для базы данных, а EntityType.t4 шаблон используется для формирования классов типов сущностей для каждой таблицы и просмотра в базе данных.

Совет

Расширение .t4 используется (вместо .tt), чтобы предотвратить преобразование шаблонов в Visual Studio. Вместо этого шаблоны будут преобразованы EF Core.

Общие сведения о T4

Давайте открываем DbContext.t4 шаблон и проверяем его содержимое. Этот файл представляет собой текстовый шаблон T4. T4 — это язык для создания текста с помощью .NET. Следующий код предназначен только для иллюстрирующих целей; Он не представляет полного содержимого файла.

Важно!

Текстовые шаблоны T4, особенно те, которые создают код, могут быть трудно читать без выделения синтаксиса. При необходимости найдите расширение в редакторе кода, который включает выделение синтаксиса 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 #>;

Первые несколько строк, начинающихся с <#@ , называются директивами. Они влияют на преобразование шаблона. В следующей таблице кратко описывается каждый тип используемой директивы.

Директива Description
template Указывает hostSpecific="true", который позволяет использовать Host свойство внутри шаблона для доступа к службам EF Core.
assembly Добавляет ссылки на сборки, необходимые для компиляции шаблона.
parameter Объявляет параметры, которые будут передаваться EF Core при преобразовании шаблона.
import Как и директивы C#, переносят пространства имен в область для кода шаблона.

После директив следующий раздел DbContext.t4 называется блоком управления. Стандартный блок управления начинается <# и заканчивается #>. Код внутри него будет выполнен при преобразовании шаблона. Список свойств и методов, доступных внутри блоков управления, см. в классе TextTransformation .

Все, что находится за пределами блока управления, будет скопировано непосредственно в выходные данные шаблона.

Блок управления выражением начинается с <#=. Код внутри него будет вычисляться, и результат будет добавлен в выходные данные шаблона. Они похожи на интерполированные строковые аргументы C#.

Более подробное и полное описание синтаксиса T4 см. в статье "Написание текстового шаблона T4".

Настройка типов сущностей

Давайте рассмотрим, как это сделать, чтобы настроить шаблон. По умолчанию EF Core создает следующий код для свойств навигации коллекции.

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

Использование List<T> является хорошим по умолчанию для большинства приложений. Однако если вы используете платформу на основе XAML, например WPF, WinUI или .NET MAUI, вместо этого необходимо включить ObservableCollection<T> привязку данных.

EntityType.t4 Откройте шаблон и найдите место его созданияList<T>. Он будет выглядеть примерно так:

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

Замените список на ObservableCollection.

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

Также необходимо добавить директиву using в шаблонный код. Использование указывается в списке в верхней части шаблона. Добавьте System.Collections.ObjectModel в список.

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

Проверьте изменения с помощью команд обратной инженерии. Шаблоны внутри проекта используются автоматически командами.

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

Если вы ранее выполнили команду, добавьте --force параметр для перезаписи существующих файлов.

Если вы сделали все правильно, теперь свойства навигации коллекции должны использоваться ObservableCollection<T>.

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

Обновление шаблонов

При добавлении шаблонов по умолчанию в проект он создает копию этих шаблонов на основе этой версии EF Core. Так как ошибки исправлены и функции добавляются в последующих версиях EF Core, шаблоны могут стать устаревшими. Необходимо просмотреть изменения, внесенные в шаблоны EF Core, и объединить их в настраиваемые шаблоны.

Одним из способов проверки изменений, внесенных в шаблоны EF Core, является использование git для сравнения версий. Следующая команда клонирует репозиторий EF Core и создает дифф этих файлов между версиями 7.0.0 и 8.0.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

Другим способом проверки изменений является скачивание двух версий Microsoft.EntityFrameworkCore.Templates из NuGet, извлечение их содержимого (можно изменить расширения файлов на ZIP-файл и сравнить эти файлы.

Прежде чем добавлять шаблоны по умолчанию в новый проект, не забудьте обновить до последнего пакета шаблонов EF Core.

dotnet new update

Расширенное использование

Игнорировать входную модель

EntityType Параметры Model представляют собой один из возможных способов сопоставления с базой данных. Вы можете игнорировать или изменять части модели. Например, имена навигации, которые мы предоставляем, могут быть не идеальными, и их можно заменить собственными при создании шаблонов кода. Другие вещи, такие как имена ограничений и фильтры индексов, используются только миграциями и могут быть безопасно опущены из модели, если вы не планируете использовать миграции с шаблонным кодом. Аналогичным образом, может потребоваться опустить последовательности или ограничения по умолчанию, если они не используются приложением.

При внесении дополнительных изменений, как это, просто убедитесь, что результирующая модель остается совместимой с базой данных. Проверка SQL, созданного с помощью dbContext.Database.GenerateCreateScript() , является хорошим способом проверки этого.

Классы конфигурации сущностей

Для больших моделей метод OnModelCreating класса DbContext может стать неуправляемым. Одним из способов решения этой проблемы является использование IEntityTypeConfiguration<T> классов. Дополнительные сведения об этих классах см. в статье "Создание и настройка модели ".

Для формирования шаблонов этих классов можно использовать третий шаблон EntityTypeConfiguration.t4. EntityType.t4 Как и шаблон, он используется для каждого типа сущности в модели и использует EntityType параметр шаблона.

Создание шаблонов других типов файлов

Основной целью реверсивного проектирования в EF Core является создание шаблонов dbContext и типов сущностей. Тем не менее, нет ничего в средствах, которые требуют, чтобы на самом деле создать шаблон кода. Например, вместо этого можно создать шаблон схемы связей сущностей с помощью 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 #>
<#
        }
    }
#>
```