Entity Framework Core - Aplicando Global Query Filters


Neste artigo vou apresentar o recurso Global Query Filters do Entity Framework Core.

O recurso Global Query Filters do Entity Framework Core também é conhecido como filtro de consulta a nível de modelo, e, permite especificar um filtro a nível de modelo que é aplicado de forma automática a todas as consultas que são executadas no contexto do tipo especificado.

Dessa forma a estrutura de entidade adiciona automaticamente o filtro na cláusula where antes de executar as consultas LINQ. Normalmente, os filtros de consulta global são aplicados no método de contexto OnModelCreating. Esses filtros também são aplicados automaticamente às consultas LINQ envolvendo tipos de entidade que são indiretamente referenciados, como os incluídos como uma propriedade de navegação.

Geralmente esse recurso é aplicado em uma exclusão reversível onde temos um campo do tipo booleano como IsDeleted.

Se você já precisou lidar com um cenário de exclusão reversível na qual você não exclui os objetos do banco de dados, em vez disso, você os marca como não sendo mais válidos, definindo as flags IsValid/IsCurrent ou Status para 0 (inativo) ou 1 (ativo) na sua aplicação.

Parece siimples mas você precisa lembrar que, ao obter objetos do banco de dados, você deve fornecer seus apenas os dados válidos - os objetos inválidos devem ser filtrados. Verificar o flag IsValid/IsCurrent/Status sempre quando o banco de dados fizer uma consulta pode levar a problemas porque você pode simplesmente esquecer de verificar o sinalizador mencionado.

Aqui entra o Globl Query Filters e opção HasQueryFilter que iremos aplicar no exemplo.

No exemplo deste artigo vou usar o banco de dados Cadastro.mdf e a tabela Paises que tem a seguinte estrutura e dados:

Na tabela Paises temos o campo IsAtivo do tipo bit que pode ser true(1) ou false(0).

Vamos aplicar o recurso Global Query Filters e ver como isso funciona na prática..

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_GlobalQueryFilter 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_GlobalQueryFilter.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_GlobalQueryFilter.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)
        {
            //-------------------------------------------------
            //aplicando o Global Query Filter ao campo IsAtivo
            //-------------------------------------------------
            modelBuilder.Entity<Pais>()
                             .HasQueryFilter(p => !p.IsAtivo);
            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.

No método OnModelCreating estamos aplicando o filtro global usando HasQueryFilter :

   modelBuilder.Entity<Pais>()
                             .HasQueryFilter(p => p.IsAtivo);

Aqui usamos a opção HasQueryFilter especificando um filtro que sempre será aplicado a todas as entidades do tipo. Primeiro você especifica o filtro da entidade e ele é aplicado automaticamente em todas as consultas quando você busca dados do banco de dados usando DbContext.

No exemplo vamos exibir somente as informações quando p.IsAtivo for igual a True ou 1.

Agora vamos testar o filtro aplicado

Definindo o código do método Main da classe Program

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

using EFCore_GlobalQueryFilter.Models;
using System.Linq;
using static System.Console;
namespace EFCore_GlobalQueryFilter
{
    class Program
    {
        static void Main(string[] args)
        {
            using (PaisDbContext context = new PaisDbContext())
            {
                WriteLine("---------------Usando Global Query Filters---------------");
                var paisesAtivos = context.Paises.ToList();
                foreach (var pais in paisesAtivos)
                {
                    WriteLine($"{pais.Id}\t{pais.Nome}");
                }
                ReadLine();
            }
        }
    }
}

Executando o projeto iremos obter o seguinte resultado:

No resultado acima vemos exibidos apenas os paises com campo IsAtivo igual a True ou valor 1.

Podemos também desabilitar o filtro global para uma consulta LINQ individual usando o método IgnoreQueryFilter().

Veja o exemplo abaixo:

using EFCore_GlobalQueryFilter.Models;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using static System.Console;
namespace EFCore_GlobalQueryFilter
{
    class Program
    {
        static void Main(string[] args)
        {
            using (PaisDbContext context = new PaisDbContext())
            {
                WriteLine("---------------Usando Global Query Filters---------------");
                var paises = context.Paises.ToList();
                foreach (var pais in paises)
                {
                    WriteLine($"{pais.Id}\t{pais.Nome}");
                }
                WriteLine("---------------Desabilitando Global Query Filters---------------");
                var todosPaises = context.Paises.IgnoreQueryFilters().ToList();
                foreach (var pais in todosPaises)
                {
                    WriteLine($"{pais.Id}\t{pais.Nome}");
                }
                ReadLine();
            }
        }
    }
}

No código acima estamos desabilitando o filtro global usando o método IgnoreQueryFilters(). Veja o resultado:

Este recurso possui as seguintes limitações:

- Não pode conter referências a propriedades de navegação
- Pode ser definido apenas no tipo de Entidade raiz de uma hierarquia de herança
- O método IgnoreQueryFilters ignora todos os filtros no tipo de entidade; ou seja, não podemos remover filtros particulares usando este método.

Pegue o projeto completo aqui :   EFCore_GlobalQueryFilter.zip

"Naquele tempo, respondendo Jesus, disse: Graças te dou, ó Pai, Senhor do céu e da terra, que ocultaste estas coisas aos sábios e entendidos, e as revelaste aos pequeninos."
Mateus 11:25

             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter

Referências:


José Carlos Macoratti