Поделиться через


Создание и настройка модели

EF Core использует модель метаданных для описания того, как типы сущностей приложения сопоставляются с базовой базой данных. Эта модель построена с помощью набора соглашений — эвристики, которые ищут общие шаблоны. Затем модель можно настроить с помощью атрибутов сопоставления (также известных как заметки данных) и (или) вызовов ModelBuilder методов (также известных как простой API) в OnModelCreating, оба из которых переопределяют конфигурацию, выполняемую соглашениями.

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

Подсказка

Примеры этой статьи можно просмотреть на сайте GitHub.

Использование fluent API для настройки модели

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

using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.FluentAPI.Required;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    #region Required
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();
    }
    #endregion
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Подсказка

Чтобы применить одну и ту же конфигурацию к нескольким объектам в модели, просмотрите массовую конфигурацию.

Настройка группировки

Чтобы уменьшить размер OnModelCreating метода, можно извлечь всю конфигурацию для типа сущности в отдельный класс, реализующий IEntityTypeConfiguration<TEntity>.

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

Затем просто вызовите Configure метод из OnModelCreating.

new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());

Применение всех конфигураций в сборке

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

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

Замечание

Порядок применения конфигураций не определен, поэтому этот метод следует использовать только в том случае, если порядок не имеет значения.

Использование EntityTypeConfigurationAttribute типов сущностей

Вместо явного вызова Configure, EntityTypeConfigurationAttribute можно поместить на тип сущности, чтобы EF Core мог найти и использовать соответствующую конфигурацию. Рассмотрим пример.

[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Isbn { get; set; }
}

Этот атрибут означает, что EF Core будет использовать указанную IEntityTypeConfiguration реализацию всякий раз, когда Book тип сущности включен в модель. Тип сущности включен в модель с помощью одного из обычных механизмов. Например, создав DbSet<TEntity> свойство для типа сущности:

public class BooksContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    //...

Или зарегистрируя его в OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>();
}

Замечание

EntityTypeConfigurationAttribute типы не будут автоматически обнаружены в сборке. Типы сущностей необходимо добавить в модель, прежде чем атрибут будет обнаружен в этом типе сущности.

Использование заметок данных для настройки модели

К классам и свойствам можно также применять определенные атрибуты (известные как заметки данных). Аннотации данных переопределяют соглашения, но будут переопределены конфигурацией Fluent API.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.DataAnnotations.Annotations;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
}

[Table("Blogs")]
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}

Встроенные соглашения

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

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

Подсказка

Приведенный ниже код поступает из ModelBuildingConventionsSample.cs.

Удаление существующего соглашения

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

Подсказка

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

Пример. Не создавайте индексы для столбцов внешнего ключа

Обычно имеет смысл создавать индексы для столбцов внешнего ключа (FK), и поэтому для этого существует встроенное соглашение: ForeignKeyIndexConvention. Просматривая представление отладки модели для типа сущности Post, у которой есть связи с Blog и Author, можно увидеть, что создаются два индекса — один для FK BlogId, а другой — для FK AuthorId.

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK Index
      BlogId (no field, int) Shadow Required FK Index
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
    Indexes:
      AuthorId
      BlogId

Однако индексы имеют издержки, и они не всегда подходят для создания всех столбцов FK. Для этого ForeignKeyIndexConvention можно удалить при создании модели:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}

Глядя на представление отладки модели Post сейчас, мы видим, что индексы на FK не созданы:

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK
      BlogId (no field, int) Shadow Required FK
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade

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

Режим отладки

Режим отладки построителя моделей доступен в отладчике вашей интегрированной среды разработки. Например, с Visual Studio:

Доступ к инструментарию отладки построителя моделей из отладчика Visual Studio

Доступ к нему также можно получить непосредственно из кода, например для отправки представления отладки в консоль:

Console.WriteLine(context.Model.ToDebugString());

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

Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));