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
Referências: