EF Core - Desabilitando o rastreamento de consultas (No Tracking)


Neste artigo vou mostrar como funciona o rastreamento de consultas e como desabilitaar este recurso para obter mais desempenho no EF Core.

Vamos iniciar mostrando como funciona o rastreamento de consultas gerenciado pelo Change Tracker.

Quando você chama o método SaveChanges no DbContext, o contexto precisa ser capaz de gerar os comandos apropriados para cada objeto que está atualmente rastreando.

Assim ele precisa saber sobre o "estado" de cada objeto - seja novo ou seja um objeto existente que tenha sido modificado de alguma forma ou se esta agendado para exclusão. O Change Tracker é o mecanismo responsável por este processo.

O Change Tracker registra o estado atual de uma entidade usando um dos quatro valores:

O rastreamento de alterações começa assim que uma entidade é carregada. Uma entidade é carregada como resultado de um retorno de uma consulta ou por estar sendo introduzida no contexto através de um dos seguintes métodos do DbContext : Add, Attach, Update e Remove, ou por ter sua propriedade de State definida na entidade de entrada retornada pela chamada do método de Entry do contexto.

Assim, o DbContext no Entity Framework é responsável pelo rastreamento das alterações feitas na entidade ou no objeto, de modo que a atualização correta é feita no banco de dados quando o método SaveChange() do contexto é chamado.

Quando recuperamos entidades usando uma consulta de objeto, o Entity Framework coloca essas entidades em um cache e rastreia quaisquer alterações feitas nessas entidades até que o método savechanges seja chamado, então Entity Framework rastreia os resultados da consulta que retornam os tipos de entidade.

Este é o comportamento padrão do EF Core. Acontece que existem cenários onde não precisamos ou não desejamos rastrear algumas entidades porque os dados serão usados apenas para fins de exibição e nenhuma operação de inclusão, alteração  ou exclusão será efetuada. Um exemplo seria a exibição dos dados em uma grade somente leitura.

Às vezes, não queremos rastrear algumas entidades porque os dados são usados apenas para fins de exibição e outras operações, como inserir, atualizar e excluir, não são feitas. Por exemplo, os dados de exibição em uma grade somente de leitura. 

Nestes cenários desabilitar o rastreamento das consultas pode ser útil pois as consultas serão executadas mais rápidamente porque não existe nenhuma necessidade de obter informações do controle de alterações de configuração.

Para desabilitar o rastreamento das consultas e assim otimizar o desempenho podemos :

Vejamos como isso funciona na prática em uma aplicação Console .NET Core usando o banco de dados Cadastro.mdf e a tabela Paises que tem a seguinte estrutura e dados:

Script SQL :   

USE [Cadastro]
GO

CREATE TABLE [dbo].[Paises](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Nome] [nvarchar](100) NOT NULL,
[Codigo] [varchar](5) NULL,
[IsAtivo] [bit] NULL,
CONSTRAINT [PK_Paises] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Recursos usados:

Criando a solução e o projeto no VS Community

Abra o VS 2017 Community e clique em Visual C# -> .NET Core e escolha o template Console App(.NET Core) :

Informe o nome EFCore_NoTracking e clique em OK;

Agora vamos incluir as seguintes referências via Nuget em nosso projeto:

No menu Tools clique em Nuget Package Manager e a seguir em Manage Nuget Packages for Solution;

Clique em Browse e localize o pacote e a seguir marque o projeto e clique em Install.

   

Repita o procedimento acima para cada pacote.

Criando o modelo de entidades

Crie uma pasta chamada Models no projeto e a seguir crie a classe Pais conforme o código mostrado a seguir:

1- Pais

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EFCore_NoTracking.Models
{
    [Table("Paises")]
    public class Pais
    {
        [Key]
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Codigo { get; set; }
        public bool IsAtivo { get; set; }
    }
}

Na pasta Models crie a classe de contexto chamada PaisDbContext que herda de DbContext e onde vamos definir o mapeamento:

5- PaisDbContext

using Microsoft.EntityFrameworkCore;
namespace EFCore_NoTracking.Models
{
    public class PaisDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Data Source=Macoratti;Initial Catalog=Cadastro;Integrated Security=True");
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
        public DbSet<Pais> Paises { get; set; }
    }
}

Nesta classe PaisDbContext definimos a propriedade Paises do tipo DbSet que vai mapear a entidade Pais para a tabela Paises.

Desabilitando o rastreamento com AsNoTracking

Vamos agora definir o código no método Main() da classe Program conforme abaixo:

using EFCore_NoTracking.Models;
using System.Linq;
using static System.Console;
namespace EFCore_NoTracking
{
    class Program
    {
        static void Main(string[] args)
        {
            using (PaisDbContext context = new PaisDbContext())
            {
                WriteLine("-------------Desbilitando o rastreamento ---------------");

                var paises = context.Paises.AsNoTracking().ToList();
                foreach (var pais in paises)
                {
                    WriteLine($"{pais.Id}\t{pais.Nome}");
                }
                ReadLine();
            }
        }
    }
}

No código estamos desabilitando o rastreamento usando o método AsNoTracking na consulta.

Executando o projeto iremos obter o seguinte resultado:

 Desabilitando o rastreamento a nível de instância de contexto

Podemos também desabilitar o rastreamento a nível de instância de contexto.

Para isso usamos a propriedade QueryTrackingBehavior da classe ChangeTracker (definida como uma propriedade da classe de contexto).

Veja o exemplo abaixo:

using EFCore_NoTracking.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using static System.Console;
namespace EFCore_NoTracking
{
    class Program
    {
        static void Main(string[] args)
        {
            using (PaisDbContext context = new PaisDbContext())
            {
                WriteLine("--- Desbilitando o rastreamento : na instância do contexto --");
                context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
                var paises2 = context.Paises.ToList();
                foreach (var pais in paises2)
                {
                    WriteLine($"{pais.Id}\t{pais.Nome}");
                }
                ReadLine();
            }
        }
    }
}

No código acima estamos desabilitando o rastreamento definindo a propriedade QueryTackingBehavior da classe ChangeTracker do contexto usando a enumeração QueryTrackingBehavior que indica como os resultados serão rastreados pelo ChangeTracker.

Os valores possíveis da enumeração QueryTackingBehavior são:

  1. NoTracking - desabilita o rastreamento das entidades retornadas pelas consultas LINQ;
  2. TrackAll - habilita o rastramento para todas as entidades que são retornadas pelas consultas LINQ

Lembre-se que você nunca deve desabilitar o rastreamento das entidades se você deseja manipular as instâncias das entidades e persistir as alterações no banco de dados usando SaveChanges().

Se o resultado de uma consulta não contém nenhuma entidade do tipo ela não será rastreada pelo Change Tracker.  Um exemplo deste cenário seria quando uma consulta retorna um tipo anônimo com algum valor da entidade :

var dados = context.Paises.Select ( p => new
{
     Pais = p.Nome,
     Ativo = p.IsAtivo
}).ToList();

Pegue o projeto completo aqui :   EFCore_NoTracking.zip

Tomando sobretudo o escudo da fé, com o qual podereis apagar todos os dardos inflamados do maligno.
Efésios 6:16

             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti