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.
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!