Introdução aos relacionamentos
Esta documentação fornece uma introdução simples à representação de relacionamentos em modelos de objetos e bancos de dados relacionais, incluindo como fazer os mapeamentos do Entity Framework Core entre os dois.
Relacionamentos em modelos de objetos
Um relacionamento define como duas entidades se relacionam entre si. Por exemplo, ao modelar postagens em um blog, cada postagem está relacionada ao blog no qual foi publicada, e o blog está relacionado a todas as postagens publicadas nesse blog.
Em uma linguagem orientada a objetos como o C#, o blog e a postagem são normalmente representados por duas classes: Blog
e Post
. Por exemplo:
public class Blog
{
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
}
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public bool Archived { get; set; }
}
Nas classes acima, não existe nada que indique que Blog
e Post
estão relacionados. Isso pode ser adicionado em um modelo de Objeto adicionando uma referência de Post
ao Blog
no qual ele está publicado:
public class Post
{
public string Title { get; set; }
public string Content { get; set; }
public DateOnly PublishedOn { get; set; }
public bool Archived { get; set; }
public Blog Blog { get; set; }
}
Da mesma forma, a direção oposta do mesmo relacionamento pode ser representada como uma coleção de objetos Post
em cada Blog
:
public class Blog
{
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
public ICollection<Post> Posts { get; }
}
Essa conexão de Blog
para Post
e, inversamente, de Post
para Blog
é conhecida como "relacionamento" no Entity Framework Core.
Importante
Um relacionamento único pode ser normalmente percorrido em qualquer direção. Neste exemplo, isso é de Blog
para Post
por meio da propriedade Blog.Posts
e de Post
de volta para Blog
por meio da propriedade Post.Blog
. Esse é um relacionamento um, não dois.
Dica
No Entity Framework Core, as propriedades Blog.Posts
e Post.Blog
são chamadas de "navegações".
Relacionamentos em bancos de dados relacionais
Os bancos de dados relacionais representam relacionamentos utilizando chaves estrangeiras. Por exemplo, usando o SQL Server ou o SQL do Azure, as tabelas a seguir podem ser utilizadas para representar nossas classes Post
e Blog
:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
[PublishedOn] datetime2 NOT NULL,
[Archived] bit NOT NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE);
CREATE TABLE [Blogs] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
[SiteUri] nvarchar(max) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([Id]));
Nesse modelo relacional, as tabelas Posts
e Blogs
recebem, cada uma, uma coluna de "chave primária". O valor da chave-valor primária identifica de forma exclusiva cada postagem ou blog. Além disso, a tabela Posts
recebe uma coluna de "chave estrangeira". A coluna Blogs
de chave primária Id
é referenciada pela coluna BlogId
de chave estrangeira da tabela Posts
. Essa coluna é "restrita" de modo que qualquer valor na coluna BlogId
de Posts
deve corresponder a um valor na coluna Id
de Blogs
. Essa correspondência determina a qual blog cada postagem está relacionada. Por exemplo, se o valor BlogId
em uma linha da tabela Posts
for 7, então a postagem representada por essa linha será publicada no blog com a chave primária 7.
Mapeamentos de relacionamentos no Entity Framework Core
O mapeamento do relacionamento do Entity Framework Core consiste em mapear a representação de chave primária/chave estrangeira usada em um banco de dados relacional para as referências entre objetos utilizados em um modelo de objeto.
No sentido mais básico, isso envolve:
- Adição de uma propriedade de chave primária para cada tipo de entidade.
- Adição de uma propriedade de chave estrangeira para um tipo de entidade.
- A associação das referências entre os tipos de entidades com as chaves primárias e estrangeiras para formar uma única configuração de relacionamento.
Uma vez feito esse mapeamento, o EF altera os valores da chave estrangeira conforme necessário quando as referências entre os objetos são alteradas e altera as referências entre os objetos conforme necessário quando os valores da chave estrangeira são alterados..
Observação
As chaves primárias são utilizadas para mais do que mapeamentos de relacionamentos. Consulte Chaves para obter mais informações.
Por exemplo, os tipos de entidade mostrados acima podem ser atualizados com propriedades de chave primária e chave estrangeira:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Uri SiteUri { get; set; }
public ICollection<Post> Posts { get; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PublishedOn { get; set; }
public bool Archived { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
Dica
As propriedades de chave primária e estrangeira não precisam ser propriedades publicamente visíveis do tipo de entidade. Entretanto, mesmo quando as propriedades estão ocultas, é importante reconhecer que elas ainda existem no modelo do EF.
A propriedade Chave primária de Blog
, Blog.Id
e a propriedade Chave estrangeira de Post
, Post.BlogId
podem então ser associadas às referências ("navegações") entre os tipos de entidade (Blog.Posts
e Post.Blog
). Isso é feito automaticamente pelo EF ao criar um relacionamento simples como esse, mas também pode ser especificado explicitamente ao substituir o método OnModelCreating
do seu DbContext
. Por exemplo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasMany(e => e.Posts)
.WithOne(e => e.Blog)
.HasForeignKey(e => e.BlogId)
.HasPrincipalKey(e => e.Id);
}
Agora, todas essas propriedades se comportarão de forma coerente como uma representação de um único relacionamento entre Blog
e Post
.
Saiba mais
A EF dá suporte a muitos diferentes tipos de relacionamentos, com muitas maneiras diferentes de representar e configurar esses relacionamentos. Para ver exemplos de diferentes tipos de relacionamentos, consulte:
- Relacionamentos de um para muitos, nos quais uma única entidade está associada a qualquer número de outras entidades.
- Relacionamentos individuais, nos quais uma única entidade está associada a outra entidade única.
- Relacionamentos de muitos-para-muitos, nos quais qualquer número de entidades está associado a qualquer número de outras entidades.
Se você é novo no EF, experimentar os exemplos relacionados nos pontos acima é uma boa maneira de ter uma ideia de como os relacionamentos funcionam.
Para se aprofundar nas propriedades dos tipos de entidades envolvidas no mapeamento de relacionamentos, consulte:
- Chaves estrangeiras e principais em relacionamentos, que aborda como as chaves estrangeiras são mapeadas para o banco de dados.
- Navegações de relacionamento, que descreve como as navegações são colocadas em camadas sobre uma chave estrangeira para fornecer uma exibição orientada a objetos do relacionamento.
Os modelos EF são criados utilizando uma combinação de três mecanismos: convenções, atributos de mapeamentos e a API do construtor de modelos. A maioria dos exemplos mostra a API de criação de modelos. Para encontrar outras opções, consulte:
- Convenções de relacionamento, que descobrem os tipos de entidades, suas propriedades e os relacionamentos entre os tipos.
- Atributos de mapeamento do relacionamento, que podem ser utilizados como alternativa à API de compilação de modelos para alguns aspectos da configuração de relacionamento.
Importante
A API de criação de modelos é a fonte final da verdade para o modelo EF, ela sempre tem precedência sobre a configuração descoberta por convenção ou especificada por atributos de mapeamento. É também o único mecanismo com total fidelidade para configurar todos os aspectos do modelo do EF.
Outros tópicos relacionados a relacionamentos incluem:
- Excluir em cascata, que descreve como as entidades relacionadas podem ser automaticamente excluídas quando
SaveChanges
ouSaveChangesAsync
é chamado. - Tipos de entidades proprietárias utilizam um tipo especial de relacionamento "proprietário" que implica uma conexão mais forte entre os dois tipos ao invés dos relacionamentos "normais" discutidos aqui. Muitos dos conceitos descritos aqui para relacionamentos normais são transferidos para relacionamentos proprietários. Entretanto, os relacionamentos com proprietários também têm seus próprios comportamentos especiais.
Dica
Consulte o glossário de termos do relacionamento conforme necessário ao ler a documentação para ajudar a entender a terminologia utilizada.
Usando relacionamentos
Os relacionamentos definidos no modelo podem ser utilizados de várias maneiras. Por exemplo:
- Os relacionamentos podem ser utilizados para consultar os dados relacionados de três maneiras:
- Antecipadamente como parte de uma consulta LINQ, utilizando
Include
. - Lento utilizando os proxies de carregamento lento, ou carregamento lento sem proxies.
- Explicitamente usando os métodos
Load
ouLoadAsync
.
- Antecipadamente como parte de uma consulta LINQ, utilizando
- Os relacionamentos podem ser utilizados na propagação de dados por meio da correspondência dos valores PK com os valores FK.
- Os relacionamentos podem ser utilizados para rastrear gráficos de entidades. Os relacionamentos são então utilizados pelo controlador de alterações para:
- Detectar alterações nos relacionamentos e fazer a correção
- Enviar atualizações de chaves estrangeiras para o banco de dados com
SaveChanges
ouSaveChangesAsync