EF Core : Configuração do modelo pré-convenção
Neste artigo veremos como fazer uma configuração do modelo pré-convenção no EF Core |
No EF Core a configuração do modelo pré-convenção refere-se à configuração de entidades e propriedades antes que o modelo seja criado pelo EF Core.
|
Isso pode ser
feito usando vários métodos, incluindo anotações de dados, API fluente e o
método OnModelCreating na classe DbContext.
Nas versões anteriores do EF Core, o mapeamento explícito era necessário para
cada propriedade quando esta diferia do padrão, incluindo detalhes como o
comprimento máximo de cadeias de caracteres e precisão decimal, bem como a
conversão de valor para o tipo de propriedade.
Isso significava
que:
- A configuração do model builder para cada propriedade
era necessária;
- Um atributo de mapeamento devia ser colocado em cada propriedade
- A iteração explícita sobre todas as propriedades de todos os tipos de entidade
e a utilização das APIs de metadados de baixo nível eram realizadas durante a
construção do modelo, que era propensa a erros e difícil de fazer com precisão,
pois a lista de tipos de entidade e propriedades mapeadas poderia estar sujeita
a alterações.
A partir do EF Core 6.0 esse processo foi simplificado permitindo que a configuração de mapeamento seja especificada uma vez para um determinado tipo e aplicada automaticamente a todas as propriedades desse tipo no modelo. que será então usado pelas convenções de construção de modelo.
Desta forma , agora o EF Core permite que a configuração de mapeamento seja especificada uma vez para um determinado tipo da CLR; essa configuração é então aplicada a todas as propriedades desse tipo no modelo à medida que são descobertas. Isso é chamado de "configuração de modelo pré-convenção", pois configura aspectos do modelo antes que as convenções de construção do modelo possam ser executadas; essa configuração é aplicada substituindo ConfigureConventions no tipo derivado de DbContext.
Para ilustrar como este recurso funciona vamos supor que temos um modelo definido com duas entidades:
public
class
Autor { public int Id { get; set; } public string? Nome { get; set; } public string? Sobrenome { get; set; } public ICollection<Livro> Livros { get; } = new List<Livro>(); } public class Livro{ public int Id { get; set; } public string? Titulo { get; set; } public DateTime PublicadoEm { get; set; } public bool EstaDisponivel { get; set; } public decimal Preco { get; set; } public Autor? Autor { get; set; } } |
Agora vamos supor que desejamos configurar as propriedades PublicadoEm, EstaDisponivel e Preco conforme abaixo:
Para aplicar estas configurações podemos agora usar o método ConfigureConventions(ModelConfigurationBuilder configurationBuilder) da classe de contexto conforme mostra o exemplo a seguir:
...
protected
override
void
ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { configurationBuilder .Properties<string>() .AreUnicode(false) .HaveMaxLength(200); configurationBuilder configurationBuilder .Properties<bool>() .HaveConversion<BoolToZeroOneConverter<int>>();
configurationBuilder |
Após definir estas configurações ao aplicar o Migrations podemos conferir no script gerado a configuração aplicada:
migrationBuilder.CreateTable( name: "Autores", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Nome = table.Column<string>(type: "varchar(200)", unicode: false, maxLength: 200, nullable: true), Sobrenome = table.Column<string>(type: "varchar(200)", unicode: false, maxLength: 200, nullable: true) }, constraints: table => { table.PrimaryKey("PK_Autores", x => x.Id); }); migrationBuilder.CreateTable( name: "Livros", columns: table => new { Id = table.Column<int>(type: "int", nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Titulo = table.Column<string>(type: "varchar(200)", unicode: false, maxLength: 200, nullable: true), PublicadoEm = table.Column<long>(type: "bigint", nullable: false), EstaDisponivel = table.Column<int>(type: "int", nullable: false), Preco = table.Column<decimal>(type: "decimal(10,3)", precision: 10, scale: 3, nullable: false), AutorId = table.Column<int>(type: "int", nullable: true) }, constraints: table => { table.PrimaryKey("PK_Livros", x => x.Id); table.ForeignKey( name: "FK_Livros_Autores_AutorId", column: x => x.AutorId, principalTable: "Autores", principalColumn: "Id"); }); |
Assim essas configurações serão aplicadas em todo o seu projeto para cada Migration aplicada e devem ser usadas quando um aspecto precisa ser configurado da mesma forma em vários tipos de entidade.
Naturalmente se você desejar sobrescrever as configurações definidas poderá usar o código padrão no método OnModelCreating:
modelBuilder.Entity<Livro>(builder => { builder .Property(x => x.Preco) .HasPrecision(12, 2); }); |
Muitos aspectos não podem ainda serem configurados com esta abordagem, e provavelmente este recurso será expandido em versões futuras.
Essa configuração é realizada antes de um modelo ser criado. Se houver algum conflito ao aplicá-lo, o rastreamento de pilha de exceção não conterá o método ConfigureConventions, portanto, pode ser mais difícil encontrar a causa.
Em outro artigo vamos continuar tratando das convenções mostrando o novo recurso do EF Core chamado Model building conventions onde podemos remover, incluir e definir configurações personalizadas.
E estamos conversados...
"Tendo sido, pois, justificados pela fé, temos paz com Deus, por nosso Senhor
Jesus Cristo;"
Romanos
5:1
Referências:
NET - Unit of Work - Padrão Unidade de ...