EF Core - Criando Views
 Neste tutorial veremos como criar views usando a abordagem Code-First no EF Core.


Em um banco de dados, uma View ou Visualização é o conjunto de resultados de uma consulta armazenada nos dados, que os usuários do banco de dados podem consultar exatamente como fariam em um objeto de coleção de banco de dados persistente.

 


Podemos pensar em View como uma tabela virtual que retorna apenas as informações necessárias de uma ou mais tabelas combinando resultados com base em consultas predefinidas.

O EF Core é um mapeador relacional de objeto (O/RM), que permite aos desenvolvedores .NET trabalhem com um banco de dados usando objetos e eliminando a necessidade da maior parte do código de acesso a dados que normalmente precisa ser escrito.

 

Para mostrar como podemos criar Views usando o EF Core na abordagem Code-First vamos criar um projeto ASP.NET Core Web APi chamada ApiViews bem simples usando o VS 2022 e o ambiente do .NET 6.

 

Vamos definir as seguintes entidades no projeto:
 

1-DespesaItem

 

public class DespesaItem
{
    public int Id { get; set; }
    public string? Nome { get; set; }
    public string? Categoria { get; set; }

    public List<DespesaHistorico> Historico { get; set; } =
        new List<DespesaHistorico>();

}

 

2-DespesaHistorico
 

public class DespesaHistorico
{
    public int Id { get; set; }
    public DateTime Data { get; set; }
    public decimal Valor { get; set; }

    public int DespesaItemId { get; set; }
    public DespesaItem? DespesaItem { get; set; }   

}


No arquivo de contexto AppDbContext temos o seguinte código:

 

using Microsoft.EntityFrameworkCore;

namespace ApiWiews.Models;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {}

    public DbSet<DespesaItem> Items { get; set; }
    public DbSet<DespesaHistorico> DespesaHistoricos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {}
}


Vamos criar o controlador DespesasController na pasta Controllers e definir apenas um método Action para retornar o total das despesas por Categoria:

 

using ApiWiews.Models;
using Microsoft.AspNetCore.Mvc;

namespace ApiWiews.Controllers;

[Route("api/[controller]")]
[ApiController]
public class DespesasController : ControllerBase
{
    private AppDbContext _context;
    public DespesasController(AppDbContext context)    
    {
        _context = context;
    }

    [HttpGet("GetDespesaPorTotal")]
    public IActionResult GetDespesaPorTotal()
    {
        var items = _context
                       .Items
                       .Select(x => new
                       {
                           id = x.Id,
                           nome = x.Nome,
                           categoria = x.Categoria,
                           total = x.Historico.Sum(r => r.Valor)
                       })
                       .OrderByDescending(x => x.total);

        return Ok(items);
    }
}


Após incluir alguns dados para testes, executando o projeto teremos o endpoint apresentado na interface do Swagger :

 

 

Acionando o endpoint teremos o seguinte resultado:

 

 

Tudo certo e dentro do esperado.


Vamos agora criar uma view e usar esta view com o objetivo de simplificar a consulta LINQ usada no método Action do controlador.

 

Adicionando uma View

Vamos começar incluindo uma nova Migration com o nome DespesaPorTotalView.

Para isso vamos aplicar o comando:  dotnet ef migration add DespesaPorTotalView

Com isso vamos criar arquivo 20220706111804_DespesaPorTotalView.cs com o seguinte código na pasta Migrations:

public partial class DespesaPorTotalView : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
    }
}

Temos os métodos Up() e Down() vazios e vamos então incluir o código para criar a view e remover a view nestes métodos.

O código usado para criar a View no SQL Server é dado a seguir:

"CREATE VIEW DespesaPorTotal
     AS SELECT d.Id, d.Nome, d.Categoria, sum(h.Valor)
     AS Total FROM Items d
     JOIN DespesaHistoricos h ON d.Id = h.DespesaItemId 
     GROUP BY d.Id,d.Nome, d.Categoria;"

Assim os métodos Up() e Down() serão definidos da seguinte forma:

public partial class DespesaPorTotalView : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("CREATE VIEW DespesaPorTotal AS SELECT d.Id, d.Nome, d.Categoria, sum(h.Valor) AS Total "
                                                 + "FROM Items d JOIN DespesaHistoricos h "
                                                 + "ON d.Id = h.DespesaItemId "
                                                 + "GROUP BY d.Id,d.Nome, d.Categoria; ");

    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"DROP VIEW DespesaPorTotal;");
    }
}

Executando o comando Migration: dotnet ef database update

Teremos a view criada no banco de dados conforme mostrado abaixo:



A seguir vamos definir a entidade DespesaPorTotal para

public class DespesaPorTotal
{
    public int Id { get; set; }
    public string? Nome { get; set; }
    public string? Categoria { get; set; }
    [Column("total")]
    public decimal Total { get; set; }
}

E vamos alterar o arquivo de contexto incluindo o mapeamento para esta entidade e definindo no método OnModelCreating o mapeamento para a View nomeada como  'despesaportotal' :

using ApiViews.Models;
using Microsoft.EntityFrameworkCore;

namespace ApiWiews.Models;

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {}

    public DbSet<DespesaPorTotal> DespesaTotais { get; set; }

    public DbSet<DespesaItem> Items { get; set; }
    public DbSet<DespesaHistorico> DespesaHistoricos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder
            .Entity<DespesaPorTotal>()
            .ToView("despesaportotal")
            .HasKey(t => t.Id);

    }
}

A seguir vamos incluir no controlador um novo método Action GetDespesaPorTotalView que agora vai usar a View criada para realizar a totalização das despesas por categoria:

using ApiWiews.Models;
using Microsoft.AspNetCore.Mvc;

namespace ApiWiews.Controllers;

[Route("api/[controller]")]
[ApiController]
public class DespesasController : ControllerBase
{

    private AppDbContext _context;
    public DespesasController(AppDbContext context)
    {
        _context = context;
    }

    [HttpGet("GetDespesaPorTotal")]
    public IActionResult GetDespesaPorTotal()

    {
        var items = _context
                       .Items
                       .Select(x => new
                       {
                           id = x.Id,
                           nome = x.Nome,
                           categoria = x.Categoria,
                           total = x.Historico.Sum(r => r.Valor)
                       })
                       .OrderByDescending(x => x.total);

        return Ok(items);
    }

    [HttpGet("GetDespesaPorTotalView")]
    public IActionResult GetDespesaPorTotalView()
    {
        var items = _context.DespesaTotais.ToList();
        return Ok(items);
    }

}

Executando o projeto teremos o seguinte resultado :

Acionando o endpoint criado teremos :

Assim temos o mesmo resultado usando a view que incluímos no banco de dados usando o EF Core.

 

Pegue o projeto aqui : ApiWiews.zip

 

"Ai dos que são sábios a seus próprios olhos, e prudentes diante de si mesmos!"
Isaías 5:21

 

Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6
Porque um menino nos nasceu, um filho se nos deu, e o principado está sobre os seus ombros, e se chamará o seu nome: Maravilhoso, Conselheiro, Deus Forte, Pai da Eternidade, Príncipe da Paz.

Isaías 9:6

Referências:


José Carlos Macoratti