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.
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.
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.
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:
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
Enderecosterá colunas comoPaisNome,PaisSigla,EstadoNome, etc. Se daqui a dois anos alguém alterar o nome do país na tabela original dePaisespara "República Federativa do Brasil", o seu registro deEnderecocontinuará intacto com o texto "Brasil", exatamente como foi gravado no dia do cadastro.
Comentários