Entity Framework Core - TrackGraph e Entidades desconectadas 


 Neste artigo veremos como tratar dados complexos em cenários desconectados usando o método TrackGraph do EF Core.


O EF Core fornece os métodos Add, Attach e Update que podem ser utilizados para o grafo de entidades. Embora estes métodos funcionem perfeitamente em cenários conectados, é possível que se queira trabalhar muitas vezes em modo desconectado e seguir todo o gráfico do objeto. Para isso podemos usar o método ChangeTracker.TrackGraph.


Nota: O ChangeTracker altera de forma automática o EntityState de cada entidade em um cenário conectado.

O método TrackGraph pode ser utilizado para rastrear um gráfico de entidade inteiro, e está disponível como parte do namespace Microsoft.EntityFrameworkCore.ChangeTracking e foi concebido para funcionar em cenários desconectados. Enquanto as entidades são recuperadas utilizando uma instância do contexto de dados, as alterações a essas entidades são armazenadas de forma persistente utilizando outra instância do contexto de dados.
 

Para acompanhar as alterações a uma entidade, o EF Core tira partido de uma propriedade chamada State que está ligada a todas as entidades rastreadas. Esta propriedade é um enumeração do tipo EntityState. Os seguintes métodos, quando utilizados, alteram o EntityState de uma entidade no gráfico da entidade :

O EF Core é hábil em gerar o SQL a ser executado com base no estado atual de um gráfo de entidades e a funcionalidade de rastreamento das entidades ou changing tracking funciona muito bem.

A seguir temos um exemplo que ilustra este recurso:

 

using (var context = new DemoDataContext())
{
    var autor = context.Autores.Single(a => a.Id == 1);
    autor.Nome = "José";
    autor.Sobrenome = "Queiroz";
    context.SaveChanges();
}


Neste trecho de código a entidade autor é recuperada dentro do bloco using e o EF Core começa a rastrear esta entidade imediatamente após a instância da entidade ser criada.


Quando o método SaveChanges é chamado na instância de contexto de dados, o EF Core verifica se foram feitas quaisquer alterações na entidade que está a ser persistida e gera a instrução SQL, conforme apropriado. No nosso exemplo, as duas propriedades Nome e Sobrenome foram atualizadas.


Ao trabalhar no modo desconectado, seria necessário avisar explicitamente o contexto de que a entidade foi modificada. Isto pode ser feito de várias maneiras diferentes. Abaixo destacamos 3 formas de fazer isso:

  1. Definir a propriedade EntityState

  2. Usar o método DbContext.Update

  3. Usar o método DbContext.Attach

1 - Definindo a propriedade EntityState

 

O estado de uma entidade determina como as entidades rastreadas serão processadas quando o método SaveChanges for chamado. Uma entidade pode estar em qualquer um dos seguintes estados:

O seguinte trecho de código ilustra como a propriedade EntityState pode ser definida como Modified:

 

public void Save(Artigo artigo)
{
    context.Entry(artigo).State = EntityState.Modified;
    context.SaveChanges();
}


Note que esta abordagem permitirá o rastreio apenas para a entidade Artigo. As entidades relacionadas da entidade  Artigo não serão rastreadas.

2 - Usando o método de Update na instância do contexto dos dados

O código a ser ilustra o uso do método Update :
 

public void Save(Artigo artigo)
{
    context.Update(artigo)
    context.SaveChanges();
}


O método Update pode ser usado para uma entidade enquanto o método UpdateRange pode ser usado para várias entidades. A chamada ao método Update marca a entidade rastreada como modified.

 

Usando o método Attach

Quando se chama o método Attach sobre uma entidade, o estado da entidade e todas as outras entidades alcançáveis tornam-se Unchanged (Inalterados).


Podemos definir explicitamente a propriedade IsModified como true  para assegurar que o contexto está ciente das propriedades da entidade que foram alteradas. O seguinte trecho de código ilustra como isto pode ser alcançado.

 

var autor = new Autor {
    Id = 1,
    Nome = "José";
    Sobrenome = "Queiroz";
};

context.Attach(autor);
context.Entry(autor).Property("Nome").IsModified = true;
context.Entry(autor).Property("Sobrenome").IsModified = true;

Neste código ambas as propriedades Nome e Sobrenome são modificadas e têm o seu estado definido como modified.

O EF Core dá suporte ao rastreamento de entidades, e , com o método TrackGraph temos uma maneira fácil de interagir com um gráfico de objetos que desejássemos que fossem rastreados.
 

Podemos assim tirar partido do método TrackGraph para acessar as entidades individuais dentro de um gráfico de objetos e depois executar código personalizado contra cada uma das entidades do gráfico.
 

Criando o projeto no VS 2019

 

Para ilustrar isso vamos criar uma aplicação Console do tipo NET Core usando o VS 2019 Community chamada EFCore_TrackGraph1.

A seguir selecione o Target Framework como .NET 5.0 (Current) conforme figura abaixo:

Clique em Create.

Vamos iniciar incluindo as referências aos seguintes pacotes do EF Core neste projeto:

Para incluir os pacotes use o menu Tools->..-> Manage Nuget Packages for Solution e na guia Browse selecione e instale os pacotes ou abra a janela Package Manager Console e digite o comando: install-package <nome-pacote>

A seguir vamos criar o seguinte modelo de entidades usadas no projeto:

 

1- Autor
 

public class Autor
{
        public int Id { get; set; }
        public string Nome { get; set; }
        public Endereco Endereco { get; set; }
        public List<Artigo> Artigos { get; set; }
}

 

2- Endereco
 

 public class Endereco
 {
        public int Id { get; set; }
        public string Local { get; set; }
        public string Cidade { get; set; }
 }

 

3- Artigo
 

 public class Artigo
 {
        public int Id { get; set; }
        public string Titulo { get; set; }
  }

Abaixo temos o diagrama de classes gerado no VS 2019 :


Agora vamos criar a classe de contexto AppDbContext :

 

using Microsoft.EntityFrameworkCore;

namespace EFCore_TrackGraph1.Data
{
    public class AppDbContext : DbContext
    {
        public AppDbContext(DbContextOptions<AppDbContext> options)
            : base(options)
        { }

         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
         {
            optionsBuilder.UseSqlServer(@"Data Source=.\;Initial Catalog=Estudo;Integrated Security=True");
         }

        public DbSet<Autor> Autores { get; set; }
        public DbSet<Endereco> Enderecos { get; set; }
        public DbSet<Artigo> Artigos { get; set; }
    }
}

 

Com isso podemos usar o o método TrackGraph conforme mostrado a seguir:

 

using EFCore_TrackGraph1.Data;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;

namespace EFCore_TrackGraph1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new AppDbContext())
            {
                var autor = new Autor()
                {
                    Id = 1,
                    Nome = "James Gosling",
                    Endereco = new Endereco()
                    {
                        Id = 1,
                        Local = "Av. Paulista 1029",
                        Cidade = "São Paulo"
                    },
                    Artigos = new List<Artigo>()
                    {
                       new Artigo(){Titulo = "Mastering Entity Framework Core" },
                       new Artigo(){Titulo = "Mastering ASP.NET Core Web API" }
                    }
                };

                context.ChangeTracker.TrackGraph(autor, a =>
                {
                    if (a.Entry.
IsKeySet)
                    {
                        a.Entry.State = EntityState.Unchanged;
                    }
                    else
                    {
                        a.Entry.State = EntityState.Added;
                    }
                });

                foreach (var entry in context.ChangeTracker.Entries())
                {
                    var entityName = entry.Entity.GetType().Name;
                    var state = entry.State.ToString();
                    Console.WriteLine($"Entity: {entityName}, State: {state}");
                }

            }
        }
    }
}

 

Inicialmente estamos povoando as entidades com alguns dados começando com a entidade Raiz que é o Autor.
 

A seguir estamos usando o método TrackGraph para o autor, e, se uma entidade tiver uma chave, o estado da entidade é marcado como "Unchanged"; Se uma entidade não tiver uma chave associada, o estado da entidade é assinalado como "Added".
 

A propriedade IsKeySet obtém um valor que indica se aos valores-chave desta entidade foram atribuídos um valor.


Concluindo usamos um laço foreach para exibir o nome da entidade e o estado das entidades que não têm uma chave.

 

Pegue o código do projeto aqui:  EFCore_TrackGraph1.zip (sem as referências)
 

"A palavra de Cristo habite em vós abundantemente, em toda a sabedoria, ensinando-vos e admoestando-vos uns aos outros, com salmos, hinos e cânticos espirituais, cantando ao Senhor com graça em vosso coração."
Colossenses 3:16

Referências:


José Carlos Macoratti