0Pricing
C# Academy · Aula

Filtros globais de consulta e entidades próprias

Aplique isolamento de locatários e exclusão lógica com filtros globais de consulta e mapeie objetos de valor usando tipos de entidade própria.

Filtros globais de consulta e entidades próprias é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

O que são filtros globais de consulta?

Filtros globais de consulta são predicados WHERE aplicados automaticamente a todas as consultas LINQ de um determinado tipo de entidade. Eles são ideais para exclusão lógica, multilocação e segurança em nível de linha — sem repetir a condição em todos os lugares.

Exclusão lógica com filtro global

Adicione um sinalizador IsDeleted e configure um filtro global para que os registros excluídos logicamente nunca sejam retornados, a menos que você ignore explicitamente o filtro.

// Entity with soft-delete flag
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public bool IsDeleted { get; set; }
}

// Configure in OnModelCreating
modelBuilder.Entity<Post>()
    .HasQueryFilter(p => !p.IsDeleted);

// All queries now implicitly add WHERE IsDeleted = 0:
var posts = await _db.Posts.ToListAsync();

Multilocação com filtro global

Use um filtro que leia o locatário atual de um serviço com escopo. Cada consulta será automaticamente limitada aos dados do locatário atual.

public class AppDbContext : DbContext
{
    private readonly ICurrentUserService _user;

    public AppDbContext(DbContextOptions<AppDbContext> opt, ICurrentUserService user)
        : base(opt) => _user = user;

    protected override void OnModelCreating(ModelBuilder mb)
    {
        mb.Entity<Order>().HasQueryFilter(
            o => o.TenantId == _user.TenantId);
    }
}
// Every Order query automatically filters by TenantId

Ignorando filtros globais

Às vezes, é necessário ignorar o filtro — por exemplo, em consultas administrativas para todos os locatários ou ao restaurar um registro excluído logicamente. Use IgnoreQueryFilters() somente nessa consulta.

// Admin: see ALL posts including deleted
var allPosts = await _db.Posts
    .IgnoreQueryFilters()
    .ToListAsync();

// Restore a specific deleted post
var deleted = await _db.Posts
    .IgnoreQueryFilters()
    .FirstOrDefaultAsync(p => p.Id == 42);

if (deleted is not null)
{
    deleted.IsDeleted = false;
    await _db.SaveChangesAsync();
}

Filtros em propriedades de navegação

Os filtros globais também participam de JOINs. Se as publicações tiverem um filtro, as publicações relacionadas carregadas por meio de Include também serão filtradas automaticamente.

// Blog with soft-delete filter on Posts
var blog = await _db.Blogs
    .Include(b => b.Posts) // only non-deleted posts are included
    .FirstOrDefaultAsync(b => b.Id == 1);

// blog.Posts contains only active posts — the filter applied in the JOIN

Tipos de entidade pertencentes: conceito

Tipos de entidade pertencentes são classes que pertencem exclusivamente a uma única entidade proprietária. Eles não têm chave primária e são armazenados na mesma tabela (por padrão). São ideais para objetos de valor em DDD.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public Address BillingAddress  { get; set; } = new();
    public Address ShippingAddress { get; set; } = new();
}

public class Address
{
    public string Street  { get; set; } = "";
    public string City    { get; set; } = "";
    public string ZipCode { get; set; } = "";
    public string Country { get; set; } = "";
}

Configurando entidades pertencentes

Use OwnsOne para configurar uma única entidade pertencente. Por padrão, o EF Core acrescenta o nome da propriedade de navegação como prefixo às colunas.

modelBuilder.Entity<Customer>(entity =>
{
    entity.OwnsOne(c => c.BillingAddress, addr =>
    {
        addr.Property(a => a.Street).HasMaxLength(300);
        addr.Property(a => a.Country).HasMaxLength(2);
    });

    entity.OwnsOne(c => c.ShippingAddress);
});
// Table: Customers with columns:
// BillingAddress_Street, BillingAddress_City, ...
// ShippingAddress_Street, ShippingAddress_City, ...

Entidades pertencentes em uma tabela separada

Use ToTable() dentro de OwnsOne para armazenar entidades pertencentes em sua própria tabela — útil quando os dados são volumosos ou opcionais.

modelBuilder.Entity<Customer>().OwnsOne(
    c => c.BillingAddress,
    addr => addr.ToTable("CustomerBillingAddresses"));

// Now BillingAddress columns are in a separate table
// with a FK back to CustomerId

OwnsMany para coleções de objetos de valor

OwnsMany configura uma coleção de entidades pertencentes — por exemplo, uma lista de endereços de e-mail ou números de telefone armazenada em uma tabela filha.

public class Customer
{
    public int Id { get; set; }
    public IList<PhoneNumber> PhoneNumbers { get; set; } = new List<PhoneNumber>();
}

public class PhoneNumber
{
    public string Number { get; set; } = "";
    public string Type   { get; set; } = "";
}

modelBuilder.Entity<Customer>()
    .OwnsMany(c => c.PhoneNumbers,
        phone => phone.ToTable("CustomerPhones"));

Herança Tabela por Hierarquia versus entidades pertencentes

Os filtros globais também funcionam bem com a herança TPH (Tabela por Hierarquia), na qual uma coluna discriminadora distingue os subtipos. Aplique um filtro com base no discriminador para consultas específicas por tipo.

// Base entity
public abstract class Payment { public int Id { get; set; } }
public class CardPayment  : Payment { public string CardLast4 { get; set; } = ""; }
public class BankPayment  : Payment { public string IBAN { get; set; } = ""; }

// All Payment subtypes in one table with a Discriminator column
// No extra config needed — EF Core handles TPH by default
var cards = await _db.Set<CardPayment>().ToListAsync();
// SQL: SELECT * FROM Payments WHERE Discriminator = 'CardPayment'

Na prática: isolamento de locatários em SaaS

Uma configuração completa de multilocação SaaS combina filtros globais de consulta com um contexto de locatário com escopo, garantindo que cada consulta seja automaticamente limitada sem código específico para cada consulta.

// Scoped tenant service
public class TenantContext
{
    public Guid TenantId { get; set; }
}

// DbContext uses it
modelBuilder.Entity<Invoice>().HasQueryFilter(
    inv => inv.TenantId == _tenantCtx.TenantId);
modelBuilder.Entity<Customer>().HasQueryFilter(
    c => c.TenantId == _tenantCtx.TenantId);

// Queries are automatically scoped:
var invoices = await _db.Invoices.ToListAsync();
// WHERE TenantId = 'current-tenant-guid'

Verificação rápida

Qual método você chama para ignorar temporariamente um filtro global de consulta em uma consulta específica?

Recapitulação: filtros globais e entidades pertencentes

Principais conclusões:

  • Os filtros globais de consulta adicionam automaticamente predicados WHERE a todas as consultas de uma entidade
  • São ideais para exclusão lógica, multilocação e segurança em nível de linha
  • Ignore-os com IgnoreQueryFilters() em operações administrativas ou de restauração
  • As entidades pertencentes modelam objetos de valor — sem PK, armazenados na tabela do proprietário
  • OwnsOne / OwnsMany configuram relacionamentos de entidades pertencentes; use ToTable() para separá-los

Perguntas Frequentes

A aula “Filtros globais de consulta e entidades próprias” é grátis?

Sim — o texto completo de “Filtros globais de consulta e entidades próprias” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Filtros globais de consulta e entidades próprias”?

Aplique isolamento de locatários e exclusão lógica com filtros globais de consulta e mapeie objetos de valor usando tipos de entidade própria. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Filtros globais de consulta e entidades próprias”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Carregamento imediato, tardio e explícito
  2. SQL bruto, procedimentos armazenados e interpolação
  3. Consultas compiladas e desempenho
  4. Filtros globais de consulta e entidades próprias
← Voltar para C# Academy