Introdução às relações
Este documento fornece uma introdução simples à representação de relações em modelos de objeto e bancos de dados relacionais, incluindo como o EF Core mapeia entre os dois.
Relações em modelos de objeto
Uma relação define como duas entidades se relacionam entre si. Por exemplo, ao modelar posts em um blog, cada post está relacionado ao blog em que é publicado, e o blog está relacionado a todos os posts publicados nesse blog.
Em uma linguagem orientada a objetos como 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 há nada que indique que Blog
e Post
estejam relacionados. Isso pode ser adicionado ao modelo de objeto adicionando uma referência de Post
ao Blog
no qual ele é 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 da mesma relação 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
de volta para Blog
é conhecida como uma "relação" no EF Core.
Importante
Uma relação singular geralmente pode ser percorrida em ambas as direções. Neste exemplo, isso é de Blog
para Post
através da propriedade Blog.Posts
e de Post
de volta para Blog
através da propriedade Post.Blog
. Esta é uma relação, não duas.
Dica
No EF Core, as propriedades Blog.Posts
e Post.Blog
são chamadas de "navegações".
Relações em bancos de dados relacionais
Os bancos de dados relacionais representam relações usando chaves estrangeiras. Por exemplo, usando o SQL Server ou o Azure SQL, as tabelas a seguir podem ser usadas 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]));
Neste modelo relacional, as tabelas Posts
e Blogs
recebem uma coluna de "chave primária". O valor da chave primária identifica exclusivamente cada postagem ou blog. Além disso, a tabela Posts
recebe uma coluna "chave estrangeira". A coluna de chave primária Blogs
Id
é referenciada pela coluna de chave estrangeira BlogId
da tabela Posts
. Esta coluna é "restrita" de tal forma 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, caso o valor BlogId
numa linha da tabela Posts
seja 7, a publicação representada por essa linha será publicada no blog com a chave primária 7.
Mapeando relacionamentos no EF Core
O mapeamento de relacionamento do EF 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 usados em um modelo de objeto.
No sentido mais básico, isto envolve:
- Adicionar uma propriedade de chave primária a cada tipo de entidade.
- Adicionar uma propriedade de chave estrangeira a um tipo de entidade.
- Associar as referências entre tipos de entidade com as chaves primária e estrangeira para formar uma única configuração de relacionamento.
Depois que esse mapeamento é feito, o EF altera os valores de chave estrangeira conforme necessário quando as referências entre objetos mudam e altera as referências entre objetos conforme necessário quando os valores de chave estrangeira são alterados.
Observação
As chaves primárias são usadas para mais do que mapear relações. 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 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. No entanto, mesmo quando as propriedades estão ocultas, é importante reconhecer que elas ainda existem no modelo EF.
A propriedade de chave primária de Blog
, Blog.Id
e a propriedade de 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 construir um relacionamento simples como este, mas também pode ser especificado explicitamente quando se sobrepõe 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 coerentemente juntas como uma representação de uma única relação entre Blog
e Post
.
Saiba mais
O EF suporta muitos tipos diferentes de relacionamentos, com muitas maneiras diferentes de representar e configurar esses relacionamentos. Para entrar em exemplos de diferentes tipos de relacionamentos, consulte:
- Relações um-para-muitos, nas quais uma única entidade está associada a qualquer número de outras entidades.
- Relações um-para-um, nas quais uma única entidade está associada a outra entidade única.
- Relações muitos-para-muitos, em que qualquer número de entidades estão associadas a qualquer número de outras entidades.
Se você é novo no EF, então tentar os exemplos vinculados nos marcadores acima é uma boa maneira de ter uma ideia de como os relacionamentos funcionam.
Para se aprofundar nas propriedades dos tipos de entidade envolvidos no mapeamento de relacionamentos, consulte:
- Chaves estrangeiras e primárias em relacionamentos, que abrange como as chaves estrangeiras são associadas ao 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 visão orientada a objeto da relação.
Os modelos EF são criados usando uma combinação de três mecanismos: convenções, atributos de mapeamento e a API do construtor de modelos. A maioria dos exemplos mostra a API de construção de modelo. Para saber mais sobre outras opções, consulte:
- Convenções de relacionamento, que descobrem tipos de entidade, suas propriedades e as relações entre os tipos.
- Atributos de mapeamento de relacionamento, que podem ser usados como uma alternativa à API de construção de modelo para alguns aspetos da configuração de relacionamento.
Importante
A API de construção de modelo é 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 aspetos do modelo EF.
Outros tópicos relacionados a relacionamentos incluem:
-
Cascade exclui, que descrevem como entidades relacionadas podem ser excluídas automaticamente quando
SaveChanges
ouSaveChangesAsync
é chamado. - Os tipos de entidade de propriedade usam um tipo especial de relação de "propriedade" que implica uma conexão mais forte entre os dois tipos do que as relações "normais" discutidas aqui. Muitos dos conceitos aqui descritos para relacionamentos normais são transferidos para relacionamentos de propriedade. No entanto, as relações de propriedade também têm seus próprios comportamentos especiais.
Dica
Consulte o glossário de termos de relacionamento conforme necessário ao ler a documentação para ajudar a entender a terminologia usada.
Usando relacionamentos
As relações definidas no modelo podem ser usadas de várias maneiras. Por exemplo:
- As relações podem ser usadas para consultar dados relacionados de uma das três formas:
-
Ansiosamente como parte de uma consulta LINQ, usando
Include
. - De forma preguiçosa usando proxies de carregamento lento ou carregamento lento sem proxies.
-
Explicitamente usando os métodos
Load
ouLoadAsync
.
-
Ansiosamente como parte de uma consulta LINQ, usando
- As relações podem ser usadas em na inicialização de dados através da correspondência de valores PK com valores FK.
- As relações podem ser usadas para rastrear grafos de entidades. Os relacionamentos são usados pelo rastreador de alterações para:
- Detetar mudanças nos relacionamentos e realizar correção
-
Enviar atualizações de chave estrangeira para o banco de dados com
SaveChanges
ouSaveChangesAsync