EF Core 2.1 - Conversão de valores


  Hoje vou apresentar a conversão de valores, o novo recurso do EF Core 2.1.

Os conversores de valores é um novo recurso do EF Core 2.1 e permite que os valores das propriedades sejam convertidos durante a leitura ou gravação no banco de dados.

Essa conversão pode ser de um valor para outro do mesmo tipo (por exemplo, cadeias de caracteres com criptografia) ou de um valor de um tipo para um valor de outro tipo (por exemplo, converter valores enum em strings no banco de dados).

Os conversores de valor são especificados em termos de um ModelClrType e um ProviderClrType. O tipo de modelo é o tipo .NET da propriedade no tipo de entidade. O tipo de provedor é o tipo .NET entendido pelo provedor de banco de dados. Por exemplo, para salvar as enumerações como strings no banco de dados, o tipo de modelo é o tipo Enum, e o tipo de provedor é String. Esses dois tipos podem ser os mesmos.

As conversões são definidas usando duas árvores de expressão Func : um da ModelClrType para ProviderClrType e o outro da ProviderClrType para ModelClrType. As expression trees são usadas para que eles possam ser compiladas em código de acesso a banco de dados para conversões eficientes. Para conversões complexas, a árvore de expressão pode ser uma chamada simples para um método que executa a conversão.

Conversores Internos

O EF Core disponibiliza um conjunto de conversores já predefinidos no namespace Microsoft.EntityFrameworkCore.Storage.ValueConversion. Os principais são:

Observe que todos os conversores internos são sem monitoração de estado e portanto, uma única instância pode ser compartilhada com segurança por várias propriedades.

Configurando os conversores de valores

Um conversor de valor é definido no método OnModelCreating da classe de contexto usando o método HasConversion que configura a propriedade de forma que o valor da propriedade é convertido para, ou a partir do, banco de dados, usando a classe ValueConverter.

A classe ValueConverter define as conversões de um objeto de um tipo em um modelo para um objeto do mesmo tipo ou de outro tipo no banco de dados.

Vejamos um exemplo prático onde vamos converter de enum para string.

Vamos supor que já temos um banco de dados chamado ABDemoDB e a tabela Alunos com a seguinte estrutura:

Na tabela Alunos a coluna Nota é do tipo char(1) e armazena as notas dos alunos que podem ser valores : A, B, C, D , E e F.

Foi definida na criação da tabela a restrição para o valor da coluna Nota estar entre A e F.

Abaixo vemos o script SQL para criar a tabela Alunos com a restrição definida :

USE [ABDemoDB]
GO
CREATE TABLE [dbo].[Alunos](
      [AlunoId] [int] IDENTITY(1,1) NOT NULL,
      [Nome] [nvarchar](100) NOT NULL,
      [Curso] [nvarchar](50) NOT NULL,
      [Nota] [char](1) NOT NULL,
CONSTRAINT [PK_Alunos] PRIMARY KEY CLUSTERED
    ([AlunoId] ASC),
           CONSTRAINT [CHK_Nota] CHECK ([Nota]='A' OR [Nota]='B'
           OR [Nota]='C' OR [Nota]='D' OR [Nota]='E' OR [Nota]='F')
);

Convertendo Enum para String

Abra o VS 2017 Community e crie um novo projeto .NET Core do tipo Console App(.NET Core) chamado EFCore_ConvertValue.

Inclua no projeto a referência para o Microsoft.EntityFrameworkCore.SqlServer :

Observe que estou usando a versão 2.21, que é a versão mais estável, mas podemos usar a versão 2.1 ou superior.

Crie no projeto a pasta Model e nesta pasta inclua duas classes:

  1. Aluno  - classe de domínio
  2. AppDbContext - classe de contexto

1- Aluno

namespace EFCore_ConvertValue.Model
{
    public enum NotaAluno
    {
        A, B, C, D, E, F
    }
    public class Aluno
    {
        public int AlunoId { get; set; }
        public string Nome { get;  set; }
        public string Curso { get; set; }
        public NotaAluno Nota { get; set; }
    }
}

Foi definida uma enumeração chamada NotaAluno com os valores de A a F que serão usados para atribuir as notas aos alunos. Dessa forma estamos restritos aos valores que foram definidos na tabela Alunos e não corremos o risco de incluir um valor fora da faixa e obter uma exceção.

No modelo de domínio Aluno definimos a propriedade Nota do tipo NotaAluno pois vamos converter a enumeração para string ao salvar a nota no banco de dados.

2- AppDbContext

using Microsoft.EntityFrameworkCore;
using System;
namespace EFCore_ConvertValue.Model
{
    public class AppDbContext : DbContext
    {
        public DbSet<Aluno> Alunos { get; set; }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Data Source=Macoratti;Initial Catalog=ABDemoDB;Integrated Security=True");
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Aluno>()
                .Property(p => p.Nota)
                  .HasConversion(typeof(string));           
         }
    }
}

No método OnModelCreating usamos o método HasConversion() na propriedade Nota da entidade Aluno para indicar a conversão que desejamos fazer : enum para string.

Existem outras maneiras de obter o mesmo resultado mas esta é a mais simples.

Apenas para constar veja outra opção de definir a conversão no método OnModelCreating():

         ....
         protected override void OnModelCreating(ModelBuilder modelBuilder)
         {
             modelBuilder.Entity<Aluno>()
                   .Property(p => p.Nota)
                   .HasConversion(
                      v => v.ToString(),
                      v => (NotaAluno)Enum.Parse(typeof(NotaAluno), v));
         }
         ...

Para vamos ver a conversão funcionando...

No arquivo Program.cs inclua o código abaixo:

using EFCore_ConvertValue.Model;
using static System.Console;
namespace EFCore_ConvertValue
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var contexto = new AppDbContext())
            {
                IncluiAluno(contexto);
                ListaAluno(contexto);
                ReadLine();
            }
        }
        private static void ListaAluno(AppDbContext contexto)
        {
            foreach(var aluno in contexto.Alunos)
            {
                WriteLine($"{aluno.Nome,10} {aluno.Curso,-15} {aluno.Nota,-10}");
            }
        }
        private static void IncluiAluno(AppDbContext contexto)
        {
            var novoAluno = new Aluno
            {
                Nome = "Vitória",
                Curso = "SQL",
                Nota = NotaAluno.D
            };
            contexto.Add(novoAluno);
            contexto.SaveChanges();
        }
    }
}

No código acima temos o método IncluiAluno() que incluin um novo aluno e que esta atribuindo o valor da enumeração NotaAluno.D à propriedade Nota.

Graças ao novo recurso do EF Core mostrado neste artigo a conversão é feita durante a inclusão como podemos ver no resultado da execução a seguir:

Espiando a tabela Alunos no banco de dados confirmamos o valor atribuído à coluna Nota:

Pegue o código completo do projeto aqui:  EFCore_ConvertValue.zip

(disse Jesus) - 'Porque o coração deste povo está endurecido,E ouviram de mau grado com seus ouvidos,E fecharam seus olhos;Para que não vejam com os olhos,E ouçam com os ouvidos,e compreendam com o coração,e se convertam,e eu os cure.'
Mateus 13:15

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti