Pular para o conteúdo principal

Subtipos em Entidades do Domínio no EFCore

Para atingir esse objetivo no Entity Framework Core, de usar subtipos em entidades, o conceito ideal é o de Owned Types (Tipos Propriedados). Quando você configura uma propriedade como Owned, o EF Core, por padrão, salva os campos dela na mesma tabela da entidade principal (como colunas adicionais).

O ponto crucial que você mencionou — "preservando os valores reais, mesmo que os registros mudem" — é resolvido perfeitamente aqui. Ao mapear essas classes como objetos de valor (Value Objects) embutidos, o EF Core vai tirar uma "foto" do estado atual do País, Estado, Cidade e Bairro no momento em que o Endereço for salvo. Eles se tornam cópias estáticas e independentes das tabelas originais.

Aqui está como estruturar e configurar isso passo a passo:

1. Criando as classes de Valor (Owned Types)

Primeiro, precisamos criar as classes que representarão os dados embutidos. Elas não precisam de um Id próprio de banco de dados para o propósito do endereço, pois farão parte da tabela Endereco.

C#
public class PaisEmbeddable
{
    public string Nome { get; set; } = string.Empty;
    public string Sigla { get; set; } = string.Empty;
}

public class EstadoEmbeddable
{
    public string Nome { get; set; } = string.Empty;
    public string Sigla { get; set; } = string.Empty;
}

public class CidadeEmbeddable
{
    public string Nome { get; set; } = string.Empty;
    public string CodigoIBGE { get; set; } = string.Empty;
}

public class BairroEmbeddable
{
    public string Nome { get; set; } = string.Empty;
}

2. Atualizando a classe Endereco

Agora, adicionamos essas classes como propriedades da sua classe Endereco.

C#
public class Endereco : ModeloAuditado
{
    public string Logradouro { get; set; } = string.Empty;
    public string Numero { get; set; } = string.Empty;
    public string Complemento { get; set; } = string.Empty;
    public string Latitude { get; set; } = string.Empty;
    public string Longitude { get; set; } = string.Empty;

    // Propriedades embutidas (Embedded / Owned)
    public PaisEmbeddable Pais { get; set; } = new();
    public EstadoEmbeddable Estado { get; set; } = new();
    public CidadeEmbeddable Cidade { get; set; } = new();
    public BairroEmbeddable Bairro { get; set; } = new();
}

3. Configurando o Mapeamento no DbContext

Para garantir que o EF Core entenda que essas propriedades devem virar colunas na tabela de Endereco (em vez de tentar criar tabelas separadas ou chaves estrangeiras), use o método OwnsOne no OnModelCreating.

C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Endereco>(builder =>
    {
        // Configura cada propriedade como "Owned"
        builder.OwnsOne(e => e.Pais, p =>
        {
            p.Property(x => x.Nome).HasColumnName("PaisNome");
            p.Property(x => x.Sigla).HasColumnName("PaisSigla");
        });

        builder.OwnsOne(e => e.Estado, est =>
        {
            est.Property(x => x.Nome).HasColumnName("EstadoNome");
            est.Property(x => x.Sigla).HasColumnName("EstadoSigla");
        });

        builder.OwnsOne(e => e.Cidade, c =>
        {
            c.Property(x => x.Nome).HasColumnName("CidadeNome");
            c.Property(x => x.CodigoIBGE).HasColumnName("CidadeCodigoIBGE");
        });

        builder.OwnsOne(e => e.Bairro, b =>
        {
            b.Property(x => x.Nome).HasColumnName("BairroNome");
        });
    });
}

Como isso funciona na prática?

Quando você for cadastrar um endereço, você buscará o País/Estado/Cidade das suas tabelas de cadastro originais (se houver) e copiará os valores para o objeto:

C#
var paisDoBanco = _context.Paises.Find(1); // Ex: Nome = "Brasil"

var novoEndereco = new Endereco
{
    Logradouro = "Av. Paulista",
    Numero = "1000",
    Pais = new PaisEmbeddable 
    { 
        Nome = paisDoBanco.Nome, // Salva "Brasil" direto na tabela Endereco
        Sigla = paisDoBanco.Sigla 
    }
    // ... preencher os outros
};

_context.Enderecos.Add(novoEndereco);
await _context.SaveChangesAsync();

💡 Resultado no Banco de Dados: A tabela Enderecos terá colunas como PaisNome, PaisSigla, EstadoNome, etc. Se daqui a dois anos alguém alterar o nome do país na tabela original de Paises para "República Federativa do Brasil", o seu registro de Endereco continuará intacto com o texto "Brasil", exatamente como foi gravado no dia do cadastro.

Comentários

Postagens mais visitadas deste blog

UML e comunicação entre Componentes

(conteúdo gerado com I.A.) Antes de abordarmos o conceito de Componentes, vale a pena analisar como um programador iniciante costuma estruturar uma aplicação. Geralmente, a escolha é construir um monólito tradicional, agrupando todas as classes de domínio (como Categoria, Produto, Compra, Venda, Pagamento, etc.) em uma única pasta de Entidades. Da mesma forma, criam-se pacotes globais para Repositórios, Serviços e Controllers. O grande problema dessa abordagem é que ela gera um forte acoplamento entre as classes e as camadas do sistema. À medida que a aplicação cresce, isso desencadeia uma série de dificuldades: Manutenção e modelagem complexas: As responsabilidades se misturam. Rapidamente, o código passa a ter diversos trechos onde as classes assumem funções que não deveriam ter, tornando a evolução do sistema confusa. Escalabilidade ineficiente: Para escalar o backend, é necessário replicar a aplicação inteira e todo o banco de dados. Além de consumir mais recursos, isso traz o ri...

Quando escolher Flask ou Quart no Python

Atualmente , considerando o suporte assíncrono já presente no Flask, a escolha entre Flask e Quart para uma API Server depende de vários fatores técnicos. Vejamos alguns critérios: Fonte:  https://pt.stackoverflow.com/questions/51268/qual-a-diferen%C3%A7a-entre-comunica%C3%A7%C3%A3o-ass%C3%ADncrona-e-s%C3%ADncrona Suporte Assíncrono : Flask : O Flask oferece suporte assíncrono por meio de extensões de terceiros, como  flask[async] . No entanto, seu desempenho assíncrono é inferior aos frameworks nativamente assíncronos. Quart : O Quart é uma reimplantação do Flask baseada no padrão ASGI (Asynchronous Server Gateway Interface). Ele utiliza E/S assíncrona em todo o código e é mais adequado para bases de código predominantemente assíncronas. Desempenho : Flask : Como aplicação WSGI, o Flask usa um único worker para cada ciclo de solicitação/resposta. Quando uma solicitação chega a uma visualização assíncrona, o Flask inicia um loop de eventos em uma thread, executa a função da vi...

Metodo para criar relacionamento entre tabelas sem dar erro cíclico EFCore

Quando estamos modelando o banco de dados no Entity Framework Core (ou em qualquer outra ferramenta) é muito comum criarmos o erro cíclico. Este erro ocorre devido ao banco de dados não conseguir determinar onde o relacionamento termina, causando um erro de loop. Vejamos uma situação real onde foi gerado esse problema: Um Professor leciona em várias Turmas, Um Professor leciona uma ou mais Disciplinas, Um Professor leciona determinadas Disciplinas em determinadas Turmas, Uma Turma tem muitos Professores, Em todas as Turmas tem 9 Disciplinas. Em todas as turmas podem ter até 9 Professores. Só por essa descrição já é possível perceber que os relacionamentos podem ser complexos de entender. E tem-se também o problema da navegação em via dupla: Professor-Turmas, Turma-Professores. Então, como modelar essas tabelas sem que o banco entre em loop? Para resolver esse problema no Entity Framework Core e evitar o erro de "Multiple Cascade Paths" (caminhos múltiplos de cascata) comum n...