Entity Framework Core patterns for data access, migrations, relationships, performance, and best practices
From dotnet-blazornpx claudepluginhub markus41/claude --plugin dotnet-blazorThis skill is limited to using the following tools:
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
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);
}
}
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"));
}
}
# 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
// 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);
}
var orders = await db.Orders
.Include(o => o.Customer)
.Include(o => o.Items)
.ThenInclude(i => i.Product)
.AsSplitQuery() // Prevents cartesian explosion
.ToListAsync(ct);
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 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);
AsNoTracking() for read-only queriesSelect() projection to only load needed columnsAsSplitQuery() for multiple includesExecuteUpdateAsync/ExecuteDeleteAsync for bulk operationsToList() before Where() (loads entire table)IQueryable in repositories, materialize in services