Criar e configurar um modelo

O EF Core usa um modelo de metadados para descrever como os tipos de entidade do aplicativo são mapeados para o banco de dados subjacente. Esse modelo é criado usando um conjunto de convenções – heurísticas que buscam padrões comuns. Em seguida, o modelo pode ser personalizado usando atributos de mapeamento (também conhecidos como anotações de dados) e/ou chamadas para os ModelBuilder métodos (também conhecidos como API Fluente), em OnModelCreating, ambos substituirão a configuração executada por convenções.

A maioria das configurações pode ser aplicada a um modelo direcionado a qualquer armazenamento de dados. Os provedores também podem habilitar a configuração específica para um armazenamento de dados específico e também podem ignorar a configuração que não tem suporte ou não é aplicável. Para obter documentação sobre configuração específica do provedor, consulte a seção Provedores de banco de dados.

Dica

Veja os exemplos deste artigo no GitHub.

Usar a API fluente para configurar um modelo

Substituía o método OnModelCreating no contexto derivado e use a API Fluente para configurar o modelo. Este é o método mais eficiente de configuração e permite que a configuração seja especificada sem modificação de suas classes de entidade. A configuração da API fluente tem a precedência mais alta e substituirá as anotações de dados e as convenções. A configuração é aplicada na ordem em que os métodos são chamados e, se houver conflitos, a chamada mais recente substituirá a configuração especificada anteriormente.

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; }
}

Dica

Para aplicar a mesma configuração a vários objetos no modelo, consulte a configuração em massa.

Configuração de agrupamento

Para reduzir o tamanho do método OnModelCreating, toda configuração de um tipo de entidade pode ser extraída para uma classe separada implementando IEntityTypeConfiguration<TEntity>.

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

Em seguida, basta invocar o método Configure de OnModelCreating.

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

Aplicar todas as configurações em um assembly

É possível aplicar todas as configurações especificadas em tipos que implementam IEntityTypeConfiguration em um determinado assembly.

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

Observação

A ordem na qual as configurações serão aplicadas é indefinida, portanto, esse método só deve ser usado quando a ordem não importar.

Usando EntityTypeConfigurationAttribute em tipos de entidade

Em vez de chamar Configure explicitamente, um EntityTypeConfigurationAttribute pode ser colocado no tipo de entidade de modo que o EF Core possa encontrar e usar a configuração apropriada. Por exemplo:

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

Esse atributo significa que o EF Core usará a implementação especificada IEntityTypeConfiguration sempre que o tipo de entidade Book for incluído em um modelo. O tipo de entidade é incluído em um modelo usando um dos mecanismos normais. Por exemplo, criando uma propriedade DbSet<TEntity> para o tipo de entidade:

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

    //...

Ou registrando-o em OnModelCreating:

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

Observação

Os tipos EntityTypeConfigurationAttribute não serão descobertos automaticamente em um assembly. Os tipos de entidades precisam ser adicionados ao modelo antes que o atributo seja descoberto nesse tipo de entidade.

Usar anotações de dados para configurar um modelo

Poderá também aplicar determinados atributos (conhecidos como Anotações de Dados) a classes e propriedades. As anotações de dados substituirão as convenções, mas serão substituídas pela configuração da API Fluente.

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; }
}

Convenções internas

O EF Core inclui muitas convenções de criação de modelos habilitadas por padrão. Encontre todas elas na lista de classes que implementam a interface IConvention. No entanto, essa lista não inclui convenções introduzidas por provedores de banco de dados e plug-ins de terceiros.

Os aplicativos podem remover ou substituir qualquer uma dessas convenções, bem como adicionar novas convenções personalizadas que aplicam a configuração para padrões que não são reconhecidos pelo EF prontos para uso.

Dica

O código mostrado abaixo vem de ModelBuildingConventionsSample.cs.

Remover uma convenção existente

Às vezes, uma das convenções internas pode não ser apropriada para seu aplicativo, caso em que pode ser removida.

Dica

Caso o modelo não use atributos de mapeamento (também conhecidos como anotações de dados) para configuração, todas as convenções com o nome terminando em AttributeConvention poderão ser removidas com segurança para acelerar a construção do modelo.

Exemplo: não criar índices para colunas de chave estrangeira

Normalmente, faz sentido criar índices para colunas de FK (chave estrangeira) e, portanto, há uma convenção interna para isso: ForeignKeyIndexConvention. Observando o modelo exibição de depuração para um tipo de entidade Post com relações com Blog e Author, podemos observar que dois índices são criados - um para o FK BlogId e outro para o 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

No entanto, os índices têm sobrecarga e nem sempre pode ser apropriado criá-los para todas as colunas de FK. Para conseguir isso, a ForeignKeyIndexConvention pode ser removida ao construir o modelo:

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

Olhando para a exibição de depuração do modelo por Post agora, vemos que os índices em FKs não foram criados:

  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

Quando desejado, os índices ainda podem ser criados explicitamente para colunas de chave estrangeira, usando o IndexAttribute ou com configuração em OnModelCreating.

Modo de depuração

O modo de depuração do construtor de modelos pode ser acessada no depurador do seu IDE. Por exemplo, com o Visual Studio:

Accessing the model builder debug view from the Visual Studio debugger

Ele também pode ser acessado diretamente do código, por exemplo, para enviar o modo de depuração para o console:

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

O modo de depuração tem um formulário curto e longo. O formulário longo também inclui todas as anotações, que podem ser úteis caso precise exibir metadados relacionais ou específicos do provedor. A exibição longa também pode ser acessada do código:

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