.NET -  Registrando entidades no DbContext
  Hoje veremos como registrar de forma dinâmica entidades no DbContext estendendo a classe ModelBuilder.


Ao utilizar o EF Core na abordagem Code-First uma das tarefas é definir no arquivo de contexto do EF Core a definição do mapeamento ORM registrando as entidades do modelo de domínio através da definição das propriedades DbSet<T>.
 


 

Isso se torna um problema para um projeto maior e complexo onde temos muitas entidades e quando temos que realizar esta tarefa manualmente. No entanto podemos adotar uma abordagem que nos ajuda a economizar tempo e linhas de código.

 

Essa abordagem consiste em criar um método de extensão para a classe ModelBuilder que usada para

definir a forma das entidades, as relações entre elas e como elas são mapeadas para o banco de dados.

 

Ao fazer isso, temos certeza de que todas as entidades serão registradas automaticamente ao fazer migrações e atualizações de banco de dados e assim neste artigo irei mostrar uma forma de estender a classe ModelBuilder
para registrar automaticamente suas entidades no banco de dados.

 

Para isso vamos criar um projeto ASP .NET Core Web API chamado ApiModelRegister no VS 2022 e criando uma pasta models no projeto vamos criar as entidades.

 

E vamos criar nesta pasta uma classe abstrata chamada ModelBase onde vamos definir a propreidade Id :

 

public abstract class ModelBase
{
    public int Id { get; set; }
}

 

A seguir todas as entidades vão herdar desta classe a propriedade Id e assim esta classe será usada para instruir a extensão do ModelBuider para incluir o registro das entidades dinamicamente quando a aplicação for inicializada.

 

A seguir vamos definir as seguintes entidades:
 

1.Cliente

 

public class Cliente : ModelBase
{
        [MaxLength(100)]
        public string? Name { get; set; }
        [MaxLength(150)]
        public string? Email { get; set; }
        public int Idade { get; set; }
    }

2.Produto

public class Produto : ModelBase
{
        [MaxLength(100)]
        public string? Nome { get; set; }
        [MaxLength(100)]
        public string? Descricao { get; set; }
        [Column(TypeName = "decimal(10, 2)")]
        public decimal Preco { get; set; }
        [Column(TypeName = "decimal(10, 2)")]
        public decimal Desconto { get; set; }
 }   

 

3.Pedido

 

public class Pedido : ModelBase
{
        public int ClienteId { get; set; }  
        public Cliente? Cliente { get; set; }        
        public DateTime DataPedido { get; set; }
        public ICollection<PedidoItem>? Itens { get; set; }
}

 

4.PedidoItem

 

public class PedidoItem :ModelBase
 {
        public int PedidoId { get; set; }
        public Pedido? Pedido { get; set; }
        public int ProdutoId { get; set; }
        public Produto? Produto { get; set; }
        public int Quantidade { get; set; }
        [Column(TypeName = "decimal(10, 2)")]
        public decimal Valor { get; set; }
 }

Com isso temos a definição das entidades do domínio onde todas herdam de classe abstrata ModelBase a propriedade Id.

Criando o método de extensão para ModelBuilder

A seguir vamos criar no projeto a pasta DataContext e criar nesta pasta o método de extensão RegisterAllEntities na classe ModelBuilderExtensions:

using Microsoft.EntityFrameworkCore;
using System.Reflection;
namespace ApiModelRegister.DataContext;
public static class ModelBuilderExtensions
{
    public static void RegisterAllEntities<BaseModel>(this ModelBuilder modelBuilder, 
                                                                         params Assembly[] assemblies)
    {
        IEnumerable<Type> types = assemblies.SelectMany
                          (a => a.GetExportedTypes())
                          .Where(c => c.IsClass && !c.IsAbstract && 
                          c.IsPublic && typeof(BaseModel).IsAssignableFrom(c));
        foreach (Type type in types)
            modelBuilder.Entity(type);
    }
}


Vamos entender o código:

- Criamos uma classe estática e um método de extenseão para a classe ModelBuilder chamada RegisterAllEntities  onde definirmos como parâmetro um array de assemblies;

- Em seguida, criamos um IEnumerable com tipo e inserimos todos os modelos que herdam da classe abstrata  ModelBase;

- Depois adicionamos cada model encontrado nos assemblies como uma entidade.

Devemos tomar cuidado ao usar o BaseModel agora, pois ele incluirá o modelo como uma entidade ao fazer migrações etc., e, como alguns modelos não são destinados à inclusão como uma entidade/tabela no banco de dados seria um erro isso ocorrer.

 

Finalmente, agora podemos adicionar nosso método de extensão no método OnModelCreating da classe de contexto que herda de DbContext.


Para registrar entidades dinamicamente no projeto em tempo de execução. Isso é feito em três linhas, conforme mostrado abaixo:

 

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        var entitiesAssembly = typeof(ModelBase).Assembly;
        modelBuilder.RegisterAllEntities<ModelBase>(entitiesAssembly);
    }
}

 

Se executarmos a migração agora iremos verificar que todas as entidades que herdam de ModelBase serão incluídas no mapeamento ORM de forma automática.

 

Como a chamada ao recurso é feita apenas uma vez quando a primeira instância de um contexto derivado é criada, o modelo é armazenado em cache para todas as outras instâncias do contexto no domínio do aplicativo.  Assim, somente se você desabilitar o cache o desempenho da aplicação ficaria prejudicado.

 

Pegue o projeto aqui: ApiModelRegister.zip 


"Não será assim entre vós; mas todo aquele que quiser entre vós fazer-se grande seja vosso serviçal;
E, qualquer que entre vós quiser ser o primeiro, seja vosso servo;"
Mateus 20:26,27
 

Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6
Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6

Referências:


José Carlos Macoratti