entity-framework

v2026.09.25

Entity Framework Core patterns for data access, migrations, relationships, performance, and best practices

GitHub
Install command
npx skhub add thelobbi/entity-framework
Markdown
SKILL.md

Entity Framework Core

DbContext Setup

public sealed class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<Product> Products => Set<Product>();
    public DbSet<Order> Orders => Set<Order>();
    public DbSet<Customer> Customers => Set<Customer>();

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

Entity Configuration (Fluent API)

public sealed class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Name).HasMaxLength(200).IsRequired();
        builder.Property(p => p.Price).HasPrecision(18, 2);
        builder.Property(p => p.Sku).HasMaxLength(50);
        builder.HasIndex(p => p.Sku).IsUnique();

        builder.HasOne(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => p.CategoryId)
            .OnDelete(DeleteBehavior.Restrict);

        builder.HasMany(p => p.Tags)
            .WithMany(t => t.Products)
            .UsingEntity(j => j.ToTable("ProductTags"));
    }
}

Migrations

# Add migration
dotnet ef migrations add InitialCreate --project Data --startup-project Web

# Update database
dotnet ef database update --project Data --startup-project Web

# Generate SQL script
dotnet ef migrations script --idempotent --project Data --startup-project Web

# Remove last migration (if not applied)
dotnet ef migrations remove --project Data --startup-project Web

Query Patterns

Basic CRUD

// Read (always use async, use AsNoTracking for reads)
var products = await db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .OrderBy(p => p.Name)
    .ToListAsync(ct);

// Read with projection (best performance)
var dtos = await db.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .Select(p => new ProductDto(p.Id, p.Name, p.Price))
    .ToListAsync(ct);

// Create
db.Products.Add(new Product { Name = "New", Price = 9.99m });
await db.SaveChangesAsync(ct);

// Update
var product = await db.Products.FindAsync([id], ct);
if (product is not null)
{
    product.Price = newPrice;
    await db.SaveChangesAsync(ct);
}

// Delete
var product = await db.Products.FindAsync([id], ct);
if (product is not null)
{
    db.Products.Remove(product);
    await db.SaveChangesAsync(ct);
}

Eager Loading

var orders = await db.Orders
    .Include(o => o.Customer)
    .Include(o => o.Items)
        .ThenInclude(i => i.Product)
    .AsSplitQuery() // Prevents cartesian explosion
    .ToListAsync(ct);

Compiled Queries (hot paths)

private static readonly Func<AppDbContext, int, CancellationToken, Task<Product?>> GetProductById =
    EF.CompileAsyncQuery((AppDbContext db, int id, CancellationToken ct) =>
        db.Products.FirstOrDefault(p => p.Id == id));

// Usage
var product = await GetProductById(db, id, ct);

Bulk Operations (.NET 10)

// Bulk update without loading entities
await db.Products
    .Where(p => p.CategoryId == oldCategoryId)
    .ExecuteUpdateAsync(s => s.SetProperty(p => p.CategoryId, newCategoryId), ct);

// Bulk delete
await db.Products
    .Where(p => p.IsDeleted && p.DeletedAt < cutoff)
    .ExecuteDeleteAsync(ct);

Performance Tips

  • Always use AsNoTracking() for read-only queries
  • Use Select() projection to only load needed columns
  • Use AsSplitQuery() for multiple includes
  • Add indexes for frequently queried columns
  • Use ExecuteUpdateAsync/ExecuteDeleteAsync for bulk operations
  • Use compiled queries for frequently executed queries
  • Never call ToList() before Where() (loads entire table)
  • Use IQueryable in repositories, materialize in services
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.25

Published

Sep 25, 2026

Category

Uncategorized

License

MIT

Source path

plugins/dotnet-blazor/skills/entity-framework

Default branch

main

Latest commit

2f1269c

Tree SHA

629e050