Skip to content

Entity Framework Core para .NET Senior

📋 Tópicos Essenciais

1. Configuração e Setup

DbContext Configuration

csharp
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.Email).IsRequired().HasMaxLength(100);
            entity.HasIndex(e => e.Email).IsUnique();
        });
    }
}

Dependency Injection

csharp
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    }
}

2. Model Configuration

Fluent API

csharp
public class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("Users");
        builder.HasKey(u => u.Id);
        builder.Property(u => u.Email).IsRequired().HasMaxLength(100);
        builder.HasIndex(u => u.Email).IsUnique();
        builder.Property(u => u.CreatedAt).HasDefaultValueSql("GETDATE()");
    }
}

Relationships

csharp
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
    public void Configure(EntityTypeBuilder<Order> builder)
    {
        builder.HasOne(o => o.Customer)
               .WithMany(c => c.Orders)
               .HasForeignKey(o => o.CustomerId)
               .OnDelete(DeleteBehavior.Restrict);
        
        builder.HasMany(o => o.OrderItems)
               .WithOne(oi => oi.Order)
               .HasForeignKey(oi => oi.OrderId);
    }
}

3. Query Performance

Eager Loading

csharp
var users = await context.Users
    .Include(u => u.Orders)
        .ThenInclude(o => o.OrderItems)
    .Include(u => u.Profile)
    .ToListAsync();

Explicit Loading

csharp
var user = await context.Users.FindAsync(1);
await context.Entry(user)
    .Collection(u => u.Orders)
    .LoadAsync();

Projection

csharp
var userDtos = await context.Users
    .Select(u => new UserDto
    {
        Id = u.Id,
        Name = u.Name,
        Email = u.Email,
        OrderCount = u.Orders.Count
    })
    .ToListAsync();

4. Advanced Querying

Raw SQL

csharp
var users = await context.Users
    .FromSqlRaw("SELECT * FROM Users WHERE CreatedAt >= {0}", startDate)
    .ToListAsync();

Stored Procedures

csharp
var users = await context.Users
    .FromSqlRaw("EXEC GetUsersByDate {0}", startDate)
    .ToListAsync();

Complex Queries

csharp
var result = await context.Orders
    .Where(o => o.CreatedAt >= startDate)
    .GroupBy(o => new { o.CustomerId, o.Status })
    .Select(g => new
    {
        CustomerId = g.Key.CustomerId,
        Status = g.Key.Status,
        TotalAmount = g.Sum(o => o.TotalAmount),
        OrderCount = g.Count()
    })
    .ToListAsync();

5. Change Tracking

Attach vs Add

csharp
// Para entidades existentes
context.Attach(user);
context.Entry(user).State = EntityState.Modified;

// Para novas entidades
context.Add(user);

Change Tracking Optimization

csharp
public async Task UpdateUserAsync(User user)
{
    var existingUser = await context.Users.FindAsync(user.Id);
    if (existingUser != null)
    {
        context.Entry(existingUser).CurrentValues.SetValues(user);
        await context.SaveChangesAsync();
    }
}

6. Bulk Operations

Bulk Insert

csharp
public async Task BulkInsertUsersAsync(IEnumerable<User> users)
{
    await context.BulkInsertAsync(users.ToList());
}

Bulk Update

csharp
public async Task BulkUpdateUsersAsync(IEnumerable<User> users)
{
    await context.BulkUpdateAsync(users.ToList());
}

7. Migrations

Creating Migrations

bash
dotnet ef migrations add InitialCreate
dotnet ef migrations add AddUserTable
dotnet ef migrations add AddOrderRelationships

Applying Migrations

bash
dotnet ef database update
dotnet ef database update --target MigrationName

Migration Scripts

bash
dotnet ef migrations script
dotnet ef migrations script --from InitialCreate --to AddUserTable

8. Performance Optimization

Query Optimization

csharp
// Use AsNoTracking for read-only queries
var users = await context.Users
    .AsNoTracking()
    .ToListAsync();

// Use compiled queries for frequently used queries
private static readonly Func<ApplicationDbContext, int, Task<User>> GetUserById =
    EF.CompileAsyncQuery((ApplicationDbContext context, int id) =>
        context.Users.FirstOrDefault(u => u.Id == id));

Connection Management

csharp
public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _context;
    
    public UnitOfWork(ApplicationDbContext context)
    {
        _context = context;
    }
    
    public async Task<int> SaveChangesAsync()
    {
        return await _context.SaveChangesAsync();
    }
    
    public void Dispose()
    {
        _context?.Dispose();
    }
}

9. Testing

In-Memory Database

csharp
public class TestDbContext : ApplicationDbContext
{
    public TestDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseInMemoryDatabase("TestDb");
        }
    }
}

Repository Testing

csharp
[Test]
public async Task GetByIdAsync_ShouldReturnUser()
{
    var options = new DbContextOptionsBuilder<ApplicationDbContext>()
        .UseInMemoryDatabase(databaseName: "TestDb")
        .Options;
    
    using var context = new ApplicationDbContext(options);
    var repository = new UserRepository(context);
    
    var user = new User { Name = "Test User", Email = "test@test.com" };
    context.Users.Add(user);
    await context.SaveChangesAsync();
    
    var result = await repository.GetByIdAsync(user.Id);
    
    Assert.IsNotNull(result);
    Assert.AreEqual(user.Name, result.Name);
}

10. Advanced Features

Global Query Filters

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasQueryFilter(u => !u.IsDeleted);
}

Value Conversions

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .Property(u => u.Status)
        .HasConversion(
            v => v.ToString(),
            v => (UserStatus)Enum.Parse(typeof(UserStatus), v));
}

Shadow Properties

csharp
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .Property<DateTime>("CreatedAt")
        .HasDefaultValueSql("GETDATE()");
}

📚 Recursos Adicionais

Livros Recomendados

  • "Entity Framework Core in Action" - Jon P Smith
  • "Programming Entity Framework" - Julia Lerman

Artigos e Blogs

  • Microsoft EF Core Documentation
  • Jon P Smith's Blog
  • Entity Framework Core GitHub Repository

⚠️ Pontos de Atenção

  • N+1 Query Problem: Use Include() adequadamente
  • Memory Usage: Use AsNoTracking() para queries somente leitura
  • Connection Pooling: Configure adequadamente
  • Migration Strategy: Planeje migrations em produção
  • Performance: Monitore queries lentas