Tipos de entidade sem chave

Observação

Esse recurso foi adicionado sob o nome "tipos de consulta". Mais tarde, ele foi renomeado como "tipos de entidade sem chave".

Além dos tipos de entidade regulares, um modelo do EF Core pode conter tipos de entidade sem chave, que podem ser usados para realizar consultas de banco de dados em dados que não contêm valores de chave.

Como definir tipos de entidade sem chave

Os tipos de entidade sem chave podem ser definidos da seguinte maneira:

[Keyless]
public class BlogPostsCount
{
    public string BlogName { get; set; }
    public int PostCount { get; set; }
}

Características de tipos de entidade sem chave

Tipos de entidade sem chave dão suporte a muitos dos mesmos recursos de mapeamento que os tipos de entidade regulares, como mapeamento de herança e propriedades de navegação. Em repositórios relacionais, eles podem configurar os objetos e colunas de banco de dados de destino por meio de métodos de API fluentes ou anotações de dados.

No entanto, eles são diferentes dos tipos de entidade regulares no que diz respeito ao fato de que eles:

  • Não podem ter uma chave definida.
  • Nunca têm as alterações monitoradas no DbContext e, portanto, nunca são inseridos, atualizados ou excluídos no banco de dados.
  • Nunca são descobertos por convenção.
  • Apenas dão suporte apenas a um subconjunto de recursos de mapeamento de navegação, especificamente:
    • Eles podem nunca agir como o fim principal de um relacionamento.
    • Eles podem não ter navegações para entidades de propriedade
    • Elas só podem conter propriedades de navegação de referência apontando para entidades regulares.
    • As entidades não podem conter propriedades de navegação para tipos de entidade sem chave.
  • Precisam ser configurados com uma anotação de dados [Keyless] ou uma chamada de método .HasNoKey().
  • Podem ser mapeados para uma consulta de definição. Uma consulta de definição é uma consulta declarada no modelo que atua como uma fonte de dados para um tipo de entidade sem chave.
  • Podem ter uma hierarquia, mas devem ser mapeados como TPH.
  • Não podem usar divisão de tabela ou divisão de entidade.

Cenários de uso

Alguns dos principais cenários de uso para tipos de entidade sem chave são:

  • Servir como o tipo de retorno para consultas SQL.
  • Mapear para modos de exibição de banco de dados que não contêm uma chave primária.
  • Mapear para tabelas que não têm uma chave primária definida.
  • Mapear para consultas definidas no modelo.

Mapear para objetos de banco de dados

O mapeamento de um tipo de entidade sem chave para um objeto de banco de dados é obtido usando o ToTable ou API fluente do ToView. Da perspectiva do EF Core, o objeto de banco de dados especificado neste método é um modo de exibição, o que significa que ele é tratado como uma fonte de consulta somente leitura e não pode ser o destino de operações de atualização, inserção ou exclusão. No entanto, isso não significa que o objeto de banco de dados seja realmente necessário para ser uma exibição de banco de dados. Como alternativa, pode ser uma tabela de banco de dados que será tratada como somente leitura. Por outro lado, para tipos de entidade comuns, o EF Core pressupõe que um objeto de banco de dados especificado no método ToTable possa ser tratado como uma tabela, oque significa que ele pode ser usado como uma fonte de consulta, mas também direcionado por operações de atualização, exclusão e inserção. Na verdade, você pode especificar o nome de um modo de exibição de banco de dados no ToTable e tudo deve funcionar bem desde que a exibição esteja configurada para ser atualizável no banco de dados.

Exemplo

O exemplo a seguir mostra como usar tipos de entidade sem chave para consultar uma exibição de banco de dados.

Dica

Veja o exemplo deste artigo no GitHub.

Primeiro, definimos um modelo simples de Blog e Postagem:

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
}

Em seguida, definimos uma exibição de banco de dados simples que nos permitirá consultar o número de postagens associadas a cada blog:

db.Database.ExecuteSqlRaw(
    @"CREATE VIEW View_BlogPostCounts AS
                SELECT b.Name, Count(p.PostId) as PostCount
                FROM Blogs b
                JOIN Posts p on p.BlogId = b.BlogId
                GROUP BY b.Name");

Em seguida, definimos uma classe para manter o resultado na exibição do banco de dados:

public class BlogPostsCount
{
    public string BlogName { get; set; }
    public int PostCount { get; set; }
}

Em seguida, configuramos o tipo de entidade sem chave em OnModelCreating usando a API HasNoKey. Usamos a API de configuração fluente para configurar o mapeamento para o tipo de entidade sem chave:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<BlogPostsCount>(
            eb =>
            {
                eb.HasNoKey();
                eb.ToView("View_BlogPostCounts");
                eb.Property(v => v.BlogName).HasColumnName("Name");
            });
}

Em seguida, configuramos o DbContext para incluir o DbSet<T>:

public DbSet<BlogPostsCount> BlogPostCounts { get; set; }

Por fim, podemos consultar a exibição do banco de dados da maneira padrão:

var postCounts = db.BlogPostCounts.ToList();

foreach (var postCount in postCounts)
{
    Console.WriteLine($"{postCount.BlogName} has {postCount.PostCount} posts.");
    Console.WriteLine();
}

Dica

Observe que também definimos uma propriedade de consulta de nível de contexto (DbSet) para atuar como uma raiz para consultas nesse tipo.

Dica

Para testar tipos de entidade sem chave mapeados para exibições usando o provedor na memória, mapeie-os para uma consulta por meio de ToInMemoryQuery. Consulte os documentos do provedor na memória para obter mais informações.