Partilhar via


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 BlogsId é referenciada pela coluna de chave estrangeira BlogId da tabela Posts. Esta coluna é "restrita" de tal forma que qualquer valor na coluna BlogId de Postsdeve 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.Ide 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:

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:

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:

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 ou SaveChangesAsync é 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: