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

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

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

Совет

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

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

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

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 не будут автоматически обнаружены в сборке. Типы сущностей необходимо добавить в модель до того, как атрибут будет обнаружен для этого типа сущности.

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

К классам и свойствам можно также применять определенные атрибуты (известные как заметки данных). Заметки к данным переопределяют соглашения, но они будут перезаписаны конфигурацией текучего 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.

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

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

Совет

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

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

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

  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:

Accessing the model builder debug view from the Visual Studio debugger

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

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

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

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