EF Core - Especificando o tipo de dados, tamanho e precisão


Hoje veremos como especificar o tipo de dados, o tamanho e a precisão usando o EF Core 5.0.

O EF Core 5.0 é a mais recente versão do EF Core lançada junto com o .NET 5.0 no dia 10 de novembro de 2020 e trás muitas melhorias e novos recursos.

Hoje vamos recordar como especificar o tipo de dados, o tamanho e a precisão usando o EF Core 5.0.

Para acompanhar o exemplo você terá que atualizar os seguintes recursos:

  1. .NET Core SDK para a versão 5.0
  2. Visual Studio Community para a versão 16.8.0 (ou superior)

Vou usar também o banco de dados SQL Server 2017 Express.

O EF core é um ORM que fornece uma abstração do banco de dados mas você tem que ficar atento ao código gerado, principalmente quando for gerar o schema do banco de dados usando as definições padrão do EF Core.

Isso deve ser feito pois  em um banco de dados relacional, usar os tipos de dados e relações corretos é muito importante. Se você não fornecer os dados necessários, o Entity Framework Core não gerará o esquema de banco de dados ideal e o desempenho do banco de dados não corresponderá às suas expectativas.

Então vamos ver como isso funciona.

Criando o projeto Console e configurando o EF Core

Vamos criar um projeto Console(.NET Core) no VS 2019 Community (16.8.1) chamado EFCore5_ConfData;

No projeto Console devemos instalar os seguintes pacotes: (estou usando a última versão estável)

Vamos definir um cenário onde temos uma entidade Exemplo definida com as seguintes propriedades:

    public class Exemplo
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public DateTime Nascimento { get; set; }
        public string Sexo { get; set; }
        public decimal Salario { get; set; }
        public long Avaliacao { get; set; }
        public int Idade { get; set; }
        public byte[] Foto { get; set; }
    }

Nosso objetivo é gerar o banco de dados e a respectiva tabela com um esquema adequado ao aplicar o Migrations.

Para poder usar o EF Core 5.0 no projeto vamos criar a classe de contexto AppDbContext em uma pasta Data do projeto:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using static System.Console;
namespace EFCore5_ConfData.Models
{
    public class AppDbContext : DbContext
    {
        public DbSet<Exemplo> Exemplos { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer("..Initial Catalog=ExemploDB;Integrated Security=True")
            .LogTo(WriteLine, new[] { RelationalEventId.CommandExecuted })
            .EnableSensitiveDataLogging();
    }
}

Neste código definimos o contexto com o EF Core e mapeamos a entidade Exemplo para gerar a tabela Exemplos no SQL Server.

Definimos o provedor do banco de dados, a string de conexão e estamos usando o novo recurso para exibir no console as consultas geradas pelo EF Core. (veremos esse recurso em outro artigo)

Neste momento podemos aplicar o Migrations emitindo os comandos na janela Package Manager Console:

add-migration Inicial
update-database

Abrindo o SQL Server Management Studio veremos o banco de dados ExemploDB criado e a tabela Exemplos gerada com o seguinte schema :

Observe o tipo de dados das colunas e note que :

Mas na verdade não precisamos nem queremos usar essas definições de tipos de dados atribuídas pelo EF Core usando as convenções padrão.

Vamos fazer então as seguintes alterações:

Especificando o tipo de dados o tamanho e a precisão

Podemos alterar as definições geradas por padrão pelo EF Core usando duas abordagens:

  1. Aplicar Data Annotations no modelo;
  2. Usar  Fluent API e especificar as definições no arquivo de contexto;

 1- Aplicando Data Annotations

Vamos incluir em nosso modelo de domínio na classe Exemplo os atributos Data Annotations conforme abaixo:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class Exemplo
{
    public int Id { get; set; }

    [MaxLength(50)]
    public string Nome { get; set; }

    [Column(TypeName = "date")]
    public DateTime Nascimento { get; set; }

    [Column(TypeName = "char(1)")]
    public string Sexo { get; set; }

    [Column(TypeName = "decimal(10,2)")]
    public decimal Salario { get; set; }

    public long Avaliacao { get; set; }
    public int Idade { get; set; }
    public byte[] Foto { get; set; }
}

Os atributos Data Annotations estão presentes nos namespaces :  System.ComponentModel.DataAnnotations

A seguir vamos aplicar o Migrations usando os comandos:

add-migration Inicial
update-database

Verificando no SQL Server Management Studio a tabela Exemplos gerada temos o resultado abaixo:

Observe que agora temos os tipos de dados aplicados da forma correta segundo a nossa especificação.

Podemos obter o mesmo resultado usando a Fluente API.

2- Usando a Fluent API

Agora vamos usar a fluente API para definir as especificações de tamanho, precisão e tipo de dados e além disso vamos alterar o comportamento para as colunas Nome e Sexo não permitindo nulos para estas colunas.

Para aplicar a fluente API podemos usar o método OnModelCreating da classe de contexto AppDbContext:

public class AppDbContext : DbContext
{
  ...
   protected override void OnModelCreating(ModelBuilder modelBuilder)
   {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Exemplo>()
                .Property(sample => sample.Nome)
                .HasMaxLength(50)
                .IsRequired();
            modelBuilder.Entity<Exemplo>()
                .Property(sample => sample.Sexo)
                .HasColumnType("char(1)")
                .IsRequired();
                
            modelBuilder.Entity<Exemplo>()
                .Property(sample => sample.Nascimento)
                .HasColumnType("date");
            modelBuilder.Entity<Exemplo>()
                .Property(sample => sample.Salario)
                 .HasColumnType("decimal(10,2)");
   }
 ...
}

A seguir vamos aplicar o Migrations usando os comandos:

add-migration Inicial
update-database

Abaixo temos o resultado obtido no SQL Server para a tabela Exemplos:

Para obter o mesmo resultado usando Data Annotations basta usar o atributo [Required] nas propriedades.

A vantagem em usar a Fluente API é que o nosso modelo de domínio e que não precisamos carregar os atributos Data Annotations e temos assim o modelo mais enxuto e livre dessa dependência.

Pegue o projeto aqui: EFCore5_ConfData.zip (sem as referências)

"Mas, se o nosso evangelho ainda está encoberto, é para os que se perdem que está encoberto, nos quais o deus deste século cegou o entendimento dos incrédulos, para que lhes não resplandeça a luz do evangelho da glória de Cristo, o qual é a imagem de Deus."
2 Coríntios 4:3,4


Referências:


José Carlos Macoratti