Формирование шаблонов (реверсивная инженерия)

Обратная инженерия — это процесс формирования классов типов сущностей и DbContext класса на основе схемы базы данных. Этот процесс можно выполнить с помощью команды Scaffold-DbContext средств консоли диспетчера пакетов (PMC) EF Core или команды dotnet ef dbcontext scaffold средств интерфейса командной строки (CLI) .NET.

Примечание.

Шаблон типов сущностей, описанных здесь, отличается от шаблонов DbContext контроллеров в ASP.NET Core с помощью Visual Studio, который здесь не описан.

Совет

Если вы используете Visual Studio, попробуйте расширение сообщества EF Core Power Tools . Эти средства предоставляют графическое средство, которое строится на основе средств командной строки EF Core и предлагает дополнительные параметры рабочего процесса и настройки.

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

  • Перед формированием шаблонов необходимо установить либо средства PMC, которые работают только в Visual Studio, либо средства .NET CLI, которые на всех платформах, поддерживаемых .NET.
  • Установите пакет NuGet для Microsoft.EntityFrameworkCore.Design в проекте, в котором будут формироваться шаблоны.
  • Установите пакет NuGet для поставщика базы данных, предназначенного для схемы базы данных, из которой требуется создать шаблон.

Обязательные аргументы

Команды PMC и .NET CLI имеют два обязательных аргумента: строка подключения в базу данных и поставщик базы данных EF Core для использования.

Connection string

Первый аргумент команды — строка подключения к базе данных. Средства будут использовать эту строку подключения для чтения схемы базы данных.

Как вы цитируете и экранируете строка подключения зависит от используемой оболочки для выполнения команды; дополнительные сведения см. в документации оболочки. Например, PowerShell требует экранирования символа $, но не символа \.

В следующем примере типы сущностей шаблонов и DbContext база Chinook данных, расположенные на экземпляре SQL Server LocalDB компьютера, используется Microsoft.EntityFrameworkCore.SqlServer поставщик базы данных.

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

Секреты пользователей для строка подключения

Если у вас есть приложение .NET, использующее модель размещения и систему конфигурации, например проект ASP.NET Core, вы можете считать строку подключения из конфигурации с помощью синтаксиса Name=<connection-string>.

Например, рассмотрим приложение ASP.NET Core со следующим файлом конфигурации:

{
  "ConnectionStrings": {
    "Chinook": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Chinook"
  }
}

Этот строка подключения в файле конфигурации можно использовать для формирования шаблонов из базы данных с помощью:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

Однако хранение строка подключения в файлах конфигурации не является хорошей идеей, так как это слишком легко случайно предоставить их, например путем отправки в систему управления версиями. Вместо этого строка подключения должны храниться в безопасном режиме, например с помощью Azure Key Vault или при локальной работе средства диспетчера секретов, а также "Секреты пользователей".

Например, чтобы использовать секреты пользователя, сначала удалите строка подключения из файла конфигурации ASP.NET Core. Затем инициализировать секреты пользователей, выполнив следующую команду в том же каталоге, что и проект ASP.NET Core:

dotnet user-secrets init

Эта команда настраивает хранилище на компьютере отдельно от исходного кода и добавляет ключ для этого хранилища в проект.

Затем сохраните строка подключения в секретах пользователя. Например:

dotnet user-secrets set ConnectionStrings:Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook"

Теперь та же команда, которая ранее использовала именованную строка подключения из файла конфигурации, будет использовать строка подключения, хранящиеся в секретах пользователей. Например:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

строки Подключение ion в шаблонном коде

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

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
    => optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");

Это делается так, чтобы созданный код не сбой при первом использовании, который был бы очень плохим опытом обучения. Однако, как говорится в предупреждении, строка подключения не должны существовать в рабочем коде. Сведения о различных способах управления строка подключения см. в разделе "Время существования DbContext", "Конфигурация" и "Инициализация".

Совет

Параметр -NoOnConfiguring (VISUAL Studio PMC) или --no-onconfiguring (.NET CLI) можно передать для подавления OnConfiguring создания метода, содержащего строка подключения.

Имя поставщика

Второй аргумент — это имя поставщика. Имя поставщика обычно совпадает с именем пакета NuGet поставщика. Например, для SQL Server или SQL Azure используйте Microsoft.EntityFrameworkCore.SqlServer.

Параметры командной строки

Процесс формирования шаблонов можно контролировать различными параметрами командной строки.

Указание таблиц и представлений

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

Аргумент -Schemas (VISUAL Studio PMC) или --schema (.NET CLI) указывает схемы таблиц и представлений, для которых будут создаваться типы сущностей. Если этот аргумент опущен, все схемы включены. Если этот параметр используется, все таблицы и представления в схемах будут включены в модель, даже если они явно не включены в -Tables модель или --table.

Аргумент -Tables (VISUAL Studio PMC) или --table (.NET CLI) указал таблицы и представления, для которых будут создаваться типы сущностей. Таблицы или представления в определенной схеме можно включить с помощью формата schema.table или schema.view. Если этот параметр не указан, будут включены все таблицы и представления. |

Например, чтобы создать шаблон только для Artists таблиц и Albums таблиц:

dotnet ef dbcontext scaffold ... --table Artist --table Album

Чтобы создать шаблон всех таблиц и представлений Customer из схем, выполните Contractor следующие действия:

dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor

Например, чтобы создать Purchases шаблон таблицы из Customer схемы, а также AccountsContracts таблицы из Contractor схемы:

dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts

Сохранение имен баз данных

Имена таблиц и столбцов по умолчанию исправляются, чтобы обеспечить более точное соответствие соглашениям об именовании .NET для типов и свойств. Указание -UseDatabaseNames (PMC) или (.NET CLI) отключает --use-database-names это поведение, сохраняя исходные имена баз данных как можно больше. Недопустимые идентификаторы .NET по-прежнему будут исправляться, а синтезированные имена, такие как свойства навигации, будут по-прежнему приводиться в соответствие с соглашениями об именовании .NET.

Например, рассмотрим следующие таблицы:

CREATE TABLE [BLOGS] (
    [ID] int NOT NULL IDENTITY,
    [Blog_Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));

CREATE TABLE [posts] (
    [id] int NOT NULL IDENTITY,
    [postTitle] nvarchar(max) NOT NULL,
    [post content] nvarchar(max) NOT NULL,
    [1 PublishedON] datetime2 NOT NULL,
    [2 DeletedON] datetime2 NULL,
    [BlogID] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);

По умолчанию следующие типы сущностей будут сформированы из следующих таблиц:

public partial class Blog
{
    public int Id { get; set; }
    public string BlogName { get; set; } = null!;
    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

public partial class Post
{
    public int Id { get; set; }
    public string PostTitle { get; set; } = null!;
    public string PostContent { get; set; } = null!;
    public DateTime _1PublishedOn { get; set; }
    public DateTime? _2DeletedOn { get; set; }
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; } = null!;
    public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}

Однако использование -UseDatabaseNames или --use-database-names результат следующих типов сущностей:

public partial class BLOG
{
    public int ID { get; set; }
    public string Blog_Name { get; set; } = null!;
    public virtual ICollection<post> posts { get; set; } = new List<post>();
}

public partial class post
{
    public int id { get; set; }
    public string postTitle { get; set; } = null!;
    public string post_content { get; set; } = null!;
    public DateTime _1_PublishedON { get; set; }
    public DateTime? _2_DeletedON { get; set; }
    public int BlogID { get; set; }
    public virtual BLOG Blog { get; set; } = null!;
}

Использование атрибутов сопоставления (ака заметки к данным)

Типы сущностей настраиваются с помощью ModelBuilder API по OnModelCreating умолчанию. Укажите -DataAnnotations (PMC) или --data-annotations (.NET Core CLI) вместо этого атрибуты сопоставления, если это возможно.

Например, при использование текучего API формируется следующий шаблон:

entity.Property(e => e.Title)
    .IsRequired()
    .HasMaxLength(160);

При использовании заметок к данным формируется следующий шаблон:

[Required]
[StringLength(160)]
public string Title { get; set; }

Совет

Некоторые аспекты модели нельзя настроить с помощью атрибутов сопоставления. Шаблон по-прежнему будет использовать API сборки модели для обработки этих случаев.

Имя DbContext

Имя класса с шаблонами DbContext будет именем суффикса базы данных, суффиксированного контекстом по умолчанию. Указать другое значение можно с помощью -Context в PMC и --context в .NET Core CLI.

Целевые каталоги и пространства имен

По умолчанию шаблоны классов сущностей и класса DbContext формируются в корневом каталоге проекта и используют пространство имен проекта.

Вы можете указать каталог, в котором формируются шаблоны классов, с помощью --output-dir. --context-dir позволяет сформировать шаблон класса DbContext в каталоге отдельно от классов типов сущностей:

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

По умолчанию для пространства имен будет указано корневое пространство имен и имена всех подкаталогов в корневом каталоге проекта. Однако можно переопределить пространство имен для всех выходных классов с помощью --namespace. Кроме того, вы можете переопределить пространство имен только для класса DbContext с помощью --context-namespace:

dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace

Шаблонный код

Результатом формирования шаблонов из существующей базы данных является:

  • Файл, содержащий класс, наследующийся от DbContext
  • Файл для каждого типа сущности

Совет

Кроме того, начиная с EF 7, вы можете настраивать созданный код с помощью текстовых шаблонов T4. Дополнительные сведения см. в статье Настраиваемые шаблоны реконструирования.

Ссылочные типы, допускающие значение NULL C#

Шаблон может создавать модели EF и типы сущностей, использующие ссылочные типы , допускающие значение NULL C# (NRTs). Использование NRT формируется автоматически при включении поддержки NRT в проекте C#, в котором выполняется формирование кода.

Например, в следующей таблице Tags содержатся как строковые столбцы, допускающие значения NULL, так и строковые столбцы, не допускающие значения NULL.

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

Это приводит к формированию соответствующих свойств строки, допускающих значения NULL и не допускающих значения NULL, в созданном классе:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Аналогичным образом следующие таблицы Posts содержат необходимую связь с Blogs таблицей:

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));

Это приводит к формированию шаблона связи между блогами, не допускающими значения NULL (обязательно).

public partial class Blog
{
    public Blog()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;

    public virtual ICollection<Post> Posts { get; set; }
}

И публикации:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

Связи "многие ко многим"

Процесс формирования шаблонов обнаруживает простые таблицы соединения и автоматически создает для них сопоставление "многие ко многим". Например, рассмотрим таблицы для Posts и Tags, а также таблицу объединения PostTag, соединяющую их:

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));

CREATE TABLE [PostTag] (
    [PostsId] int NOT NULL,
    [TagsId] int NOT NULL,
    CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
    CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);

При создании шаблонов это приводит к классу для Post:

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

И класса для Tag:

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

Но для таблицы PostTag не существует класса. Вместо этого конфигурация для связи "многие ко многим" формируется следующим образом:

entity.HasMany(d => d.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity<Dictionary<string, object>>(
        "PostTag",
        l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
        r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
        j =>
            {
                j.HasKey("PostsId", "TagsId");
                j.ToTable("PostTag");
                j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
            });

Другие языки программирования

Пакеты EF Core, опубликованные кодом C#, опубликованные корпорацией Майкрософт. Однако базовая система шаблонов поддерживает модель подключаемого модуля для формирования шаблонов на других языках. Эта модель подключаемого модуля используется различными проектами, выполняемыми сообществом, например:

Настройка кода

Начиная с EF7, одним из лучших способов настройки созданного кода является настройка шаблонов T4, используемых для его создания.

Код также можно изменить после создания, но лучший способ сделать это зависит от того, планируется ли повторно запустить процесс формирования шаблонов при изменении модели базы данных.

Шаблон только один раз

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

Синхронизация базы данных и модели EF можно выполнить одним из двух способов:

  • Переключитесь на миграцию базы данных EF Core и используйте типы сущностей и конфигурацию модели EF в качестве источника истины, используя миграции для управления схемой.
  • Вручную обновите типы сущностей и конфигурацию EF при изменении базы данных. Например, если новый столбец добавляется в таблицу, добавьте свойство для столбца в сопоставленный тип сущности и добавьте любую необходимую конфигурацию с помощью атрибутов сопоставления и (или) кода.OnModelCreating Это относительно просто, с единственной реальной проблемой является процесс, чтобы убедиться, что изменения базы данных записываются или обнаруживаются каким-то образом, чтобы разработчики, ответственные за код, могли реагировать.

Повторяющееся формирование шаблонов

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

[СОВЕТ] По умолчанию команды EF не перезаписывают существующий код для защиты от случайной потери кода. Аргумент -Force (VISUAL Studio PMC) или --force (.NET CLI) можно использовать для принудительного перезаписи существующих файлов.

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

  • DbContext Класс и классы сущностей создаются как частичные. Это позволяет вводить дополнительные элементы и код в отдельный файл, который не переопределяется при запуске шаблонов.
  • Класс DbContext содержит частичный метод OnModelCreatingPartial. Реализацию этого метода можно добавить в частичный класс для объекта DbContext. Затем он будет вызываться после OnModelCreating вызова.
  • Конфигурация модели, созданная с помощью ModelBuilder API, переопределяет любую конфигурацию, выполняемую соглашениями или атрибутами сопоставления, а также более раннюю конфигурацию, выполненную в построителе моделей. Это означает, что код OnModelCreatingPartial можно использовать для переопределения конфигурации, созданной процессом формирования шаблонов, без необходимости удалить эту конфигурацию.

Наконец, помните, что начиная с EF7 шаблоны T4, используемые для создания кода, можно настроить. Это часто более эффективный подход, чем формирование шаблонов по умолчанию, а затем изменение с частичными классами и методами.

Как это работает

Реконструирование начинается с чтения схемы базы данных. При этом считываются сведения о таблицах, столбцах, ограничениях и индексах.

Затем на основе сведений о схеме создается модель EF Core. С помощью таблиц создаются типы сущностей, с помощью столбцов — свойства, а с помощью внешних ключей — связи.

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

Ограничения

  • Не все сведения о модели можно представить с помощью схемы базы данных. Например, в схеме базы данных нет сведений об иерархиях наследования, принадлежащих типах и разделении таблиц. Из-за этого эти конструкции никогда не будут шаблонными.
  • Кроме того, поставщик EF Core может не поддерживать некоторые типы столбцов. Эти столбцы не будут включены в модель.
  • Маркеры параллелизма можно определить в модели EF Core, чтобы предотвратить одновременное обновление одной сущности двумя пользователями. Некоторые базы данных имеют специальный тип для представления этого типа столбца (например, rowversion в SQL Server), в этом случае мы можем перепроектировать эти сведения; однако другие маркеры параллелизма не будут шаблонными.