Blog

Domine a plataforma e otimize seus gastos!

Trabalhando com Views de banco de dados no EF Core

Trabalhando com Views de banco de dados no EF Core
May 10, 2024
Photo by Jan Antonin Kolar / Unsplash

O Entity Framework Core é um framework ORM (Object-Relational Mapper) que facilita o acesso e a manipulação de dados em bancos de dados relacionais a partir de código .NET. Uma das características poderosas do EF Core é sua capacidade de mapear entidades de banco de dados para classes .NET e vice-versa.

Neste artigo, vamos explorar a integração das views do banco de dados diretamente no código-fonte das nossas aplicações, utilizando as migrações. Essa abordagem pode ser uma estratégia eficaz para evitar a necessidade de versionar as views separadamente do código-fonte, simplificando assim o processo de desenvolvimento e manutenção.

Vamos criar inicialmente um diretório para o nosso projeto:

mkdir EfCoreViews

Como iremos fazer uma integração com o EF Core, subiremos um banco de dados por meio de um docker compose.

version: '3.8'
services:
  database:
    container_name: "sqlserver"
    hostname: "sqlserver"
    ports:
      - "1433:1433"
    image: "mcr.microsoft.com/azure-sql-edge:latest"
    environment:
      ACCEPT_EULA: "1"
      MSSQL_SA_PASSWORD: "Teste12345!"
    

Vamos ainda criar o versionamento via git no diretório raiz.

dotnet new gitignore
git init

Com as dependências prontas, já podemos criar o projeto do tipo console e adicionar alguns pacotes NuGet a esse projeto criado.

dotnet new console
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design

A ideia desse post é criar um pequeno projeto que utiliza views para simplificar as queries. Por isso, vamos criar uma modelagem simples de dados: Iremos ter uma entidade chamada Author que possui uma lista de Books:

namespace EfCoreViews.Models;

public class Author
{
    public Guid Id { get; set; }
    public string Name { get; set; } = default!;
    public IList<Book> Books { get; set; } = new List<Book>();
}
namespace EfCoreViews.Models;

public class Book
{
    public Guid Id { get; set; }
    public string Name { get; set; } = default!;
    public Guid AuthorId { get; set; } = default!;
    public Author Author { get; set; } = default!;
}

Com as entidades prontas, podemos criar suas configurações para o code-first:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EfCoreViews.Models.Configurations;

public class AuthorConfiguration :  IEntityTypeConfiguration<Author>
{
    public void Configure(EntityTypeBuilder<Author> builder)
    {
        builder.HasKey(e => e.Id);
        
        builder.Property(e => e.Name)
            .IsRequired();

        builder.HasData(new List<Author>
        {
            new()
            {
                Id = Guid.Parse("92338F02-F3A5-4C82-AC9F-F9DC1A8D7256"),
                Name = "J. R. R. Tolkien"
            },
            new()
            {
                Id = Guid.Parse("D7890984-FC4E-4AFA-8FC4-530F4C684A26"),
                Name = "James Carroll"
            }
        });
    }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EfCoreViews.Models.Configurations;

public class BookConfiguration :  IEntityTypeConfiguration<Book>
{
    public void Configure(EntityTypeBuilder<Book> builder)
    {
        builder.HasKey(e => e.Id);
        
        builder.Property(e => e.Name)
            .IsRequired();

        builder.HasOne(x => x.Author)
            .WithMany(x => x.Books)
            .HasForeignKey(x => x.AuthorId);
        
        builder.HasData(new List<Book>
        {
            new()
            {
                Id = Guid.Parse("2AE6873C-31BC-48DE-85F9-C63289A312B3"),
                AuthorId = Guid.Parse("92338F02-F3A5-4C82-AC9F-F9DC1A8D7256"),
                Name = "The lord of the rings"
            },
            new()
            {
                Id = Guid.Parse("72C6EC9B-3964-4272-BED5-4D460020B79B"),
                AuthorId = Guid.Parse("92338F02-F3A5-4C82-AC9F-F9DC1A8D7256"),
                Name = "The hobbit"
            }
        });
    }
}

Apenas um detalhe. Fizemos o seed de alguns dados!

Para finalizar as configurações do EF Core, precisamos criar a classe principal dessa ferramenta, o AppDbContext que herda de DbContext:

using EfCoreViews.Models;
using Microsoft.EntityFrameworkCore;

namespace EfCoreViews;

public class AppDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer(@"Server=localhost;Database=MyDb;User Id=Sa;Password=Teste12345!;TrustServerCertificate=true");
    }
    
     public DbSet<Book> Books { get; set; } = null!;
     public DbSet<Author> Authors { get; set; } = null!;
    

     protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
    }
}

Vamos criar a migration e aplicá-la ao banco de dados.

dotnet ef migrations add InitialCreate
dotnet ef database update 

Com os comandos finalizados, podemos ver que as tabelas foram criadas!

Obs: Estou utilizando o Azure Data Studio!

Na classe Program.cs, podemos criar um pequeno código para fazer uma query que utiliza algumas manipulações extras de dados (Include e Select).

// See https://aka.ms/new-console-template for more information

using EfCoreViews;
using Microsoft.EntityFrameworkCore;

await using var context = new AppDbContext();

var results = await context.Authors
    .Include(x => x.Books)
    .Select(x => new
    {
        Author = x,
        BookCount = x.Books.Count
    })
    .ToListAsync();

foreach (var result in results)
{
    Console.WriteLine($"Author: {result.Author.Name} -> Book count: {result.BookCount}");
}

Temos o resultado:

A ideia agora é criar uma view que realiza essa mesma query. Para que essa view seja versionada juntamente com o código-fonte, vamos criar uma migration vazia por meio do seguinte comando:

 dotnet ef migrations add AddView
 

Vamos aprimorar seu conteúdo com o seguinte texto:

using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace EfCoreViews.Migrations
{
    /// <inheritdoc />
    public partial class AddView : Migration
    {
        /// <inheritdoc />
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql(@"
            CREATE VIEW 
                AuthorBookCounting
            AS
            SELECT 
                a.Name AS Author,
                COUNT(b.Id) AS BookCount
            FROM 
                Authors a
            LEFT JOIN
                Books b ON b.AuthorId = a.Id
            GROUP BY 
                a.Name;
                ");
        }

        /// <inheritdoc />
        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql("DROP VIEW AuthorBookCounting");
        }
    }
}

Observe que no método Up, criamos a view. E no método Down, a deletamos.

Para servir como retorno da view, vamos criar um modelo chamado AuthorBookCount.

namespace EfCoreViews.Models;

public class AuthorBookCount
{
    public Guid Id { get; set; }
    public string Author { get; set; } = default!;
    public int BookCount { get; set; } = default!;
}

el

Precisamos ainda acionar um novo DbSet ao AppDbContex, e a configuração (code-first) do modelo AuthorBookCount.

public DbSet<AuthorBookCount> AuthorBookCount { get; set; } = null!;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace EfCoreViews.Models.Configurations;

public class AuthorBookCountConfigurationConfiguration :  IEntityTypeConfiguration<AuthorBookCount>
{
    public void Configure(EntityTypeBuilder<AuthorBookCount> builder)
    {
        builder.HasKey(e => e.Id);
        builder.ToView("AuthorBookCounting");
    }
}

Com essas modificações prontas, vamos modificar o código do Program.cs:

using EfCoreViews;
using Microsoft.EntityFrameworkCore;

await using var context = new AppDbContext();

var results = await context.AuthorBookCount.ToListAsync();

foreach (var result in results)
{
    Console.WriteLine($"Author: {result.Author} -> Book count: {result.BookCount}");
}

Observe que realizamos a query por meio da view AuthorBookCount!

Você já pode baixar o projeto por esse link, e não esquece de me seguir no LinkedIn!

Até a próxima, abraços!

Podemos te ajudar com uma revisão 100% gratuita do seu ambiente cloud.

Agendar a revisão gratuita do meu ambiente

Share this post

Inscreva-se para novas postagens

Inscreva-se