
Efcore Patterns
Apply EF Core conventions—NoTracking defaults, query splitting, and safe migration workflows—when scaffolding or hardening a .NET data layer.
Overview
efcore-patterns is an agent skill for the Build phase that applies Entity Framework Core performance and migration conventions to DbContext setup, queries, and schema change workflows.
Install
npx skills add https://github.com/aaronontheweb/dotnet-skills --skill efcore-patternsWhat is this skill?
- NoTracking-by-default DbContext setup with explicit AsTracking for writes
- Query splitting guidance for loading multiple navigation collections without cartesian explosion
- Migration rules: CLI-only changes, no hand-edited migration files
- Dedicated migration service pattern instead of running migrations on app startup
- ExecutionStrategy and explicit update patterns for transient failures and untracked entities
- Five documented core principles (NoTracking default, CLI-only migrations, dedicated migration service, execution strateg
Adoption & trust: 1.1k installs on skills.sh; 989 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are shipping EF Core without consistent tracking, migration, or load strategies and keep hitting slow queries, risky startups, or confusing update behavior.
Who is it for?
Solo builders on ASP.NET Core or Aspire who own the data layer and want opinionated EF Core defaults before traffic hits production.
Skip if: Teams on Dapper-only stacks, greenfield projects with no relational persistence yet, or apps where a DBA owns all migration policy outside the repo.
When should I use this skill?
Setting up EF Core in a new project, optimizing query performance, managing database migrations, integrating EF Core with .NET Aspire, debugging change tracking issues, or loading multiple navigation collections efficien
What do I get? / Deliverables
You leave with copy-ready patterns for DbContext defaults, safer migrations, and explicit update paths that align with how your app actually reads and writes data.
- DbContext configuration snippets aligned to NoTracking and explicit tracking
- Migration and dedicated migration-service layout recommendations
Recommended Skills
Journey fit
Database access patterns and migration discipline belong on the build shelf because they shape how the backend persists and evolves schema during implementation. Backend subphase is the canonical home for DbContext configuration, LINQ performance, and migration services separate from API route design.
How it compares
Use as a procedural EF Core playbook instead of copying scattered Stack Overflow snippets into every new service.
Common Questions / FAQ
Who is efcore-patterns for?
It is for indie and solo .NET developers implementing or refactoring EF Core in APIs, workers, or Aspire-hosted services who need consistent query and migration habits.
When should I use efcore-patterns?
Use it during Build backend work when setting up a new DbContext, optimizing read-heavy LINQ, splitting queries across navigations, wiring a migration runner, or debugging tracking after adopting NoTracking defaults.
Is efcore-patterns safe to install?
It is documentation-style guidance without mandatory shell or network hooks, but review the Security Audits panel on this Prism page before adding any skill repo to your agent environment.
SKILL.md
READMESKILL.md - Efcore Patterns
# Entity Framework Core Patterns ## When to Use This Skill Use this skill when: - Setting up EF Core in a new project - Optimizing query performance - Managing database migrations - Integrating EF Core with .NET Aspire - Debugging change tracking issues - Loading multiple navigation collections efficiently (query splitting) ## Core Principles 1. **NoTracking by Default** - Most queries are read-only; opt-in to tracking 2. **Never Edit Migrations Manually** - Always use CLI commands 3. **Dedicated Migration Service** - Separate migration execution from application startup 4. **ExecutionStrategy for Retries** - Handle transient database failures 5. **Explicit Updates** - When NoTracking, explicitly mark entities for update --- ## Pattern 1: NoTracking by Default Configure your DbContext to disable change tracking by default. This improves performance for read-heavy workloads. ```csharp public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { // Disable change tracking by default for better performance on read-only queries // Use .AsTracking() explicitly for queries that need to track changes ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; } public DbSet<Order> Orders => Set<Order>(); public DbSet<Customer> Customers => Set<Customer>(); } ``` ### When NoTracking is Active **Read-only queries work normally:** ```csharp // ✅ Fast read - no tracking overhead var orders = await dbContext.Orders .Where(o => o.Status == OrderStatus.Pending) .ToListAsync(); ``` **Writes require explicit handling:** ```csharp // ❌ WRONG - Entity not tracked, SaveChanges does nothing var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.Id == orderId); order.Status = OrderStatus.Shipped; await dbContext.SaveChangesAsync(); // Nothing happens! // ✅ CORRECT - Explicitly mark entity for update var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.Id == orderId); order.Status = OrderStatus.Shipped; dbContext.Orders.Update(order); // Marks entire entity as modified await dbContext.SaveChangesAsync(); // ✅ ALSO CORRECT - Use AsTracking() for the query var order = await dbContext.Orders .AsTracking() .FirstOrDefaultAsync(o => o.Id == orderId); order.Status = OrderStatus.Shipped; await dbContext.SaveChangesAsync(); // Works! ``` ### When to Use Tracking | Scenario | Use Tracking? | Why | |----------|---------------|-----| | Display data in UI | No | Read-only, no updates | | API GET endpoints | No | Returning data, no mutations | | Update single entity | Yes or explicit Update() | Need to save changes | | Complex update with navigation | Yes | Tracking handles relationships | | Batch operations | No + ExecuteUpdate | More efficient | ### Explicit Add/Update Pattern ```csharp public class OrderService { private readonly ApplicationDbContext _db; // CREATE - Always use Add (works regardless of tracking) public async Task<Order> CreateOrderAsync(Order order) { _db.Orders.Add(order); await _db.SaveChangesAsync(); return order; } // UPDATE - Explicitly mark as modified public async Task UpdateOrderStatusAsync(Guid orderId, OrderStatus newStatus) { var order = await _db.Orders.FirstOrDefaultAsync(o => o.Id == orderId) ?? throw new NotFoundException($"Order {orderId} not found"); order.Status = newStatus; order.UpdatedAt = DateTimeOffset.UtcNow; // Explicitly mark as modified since DbContext uses NoTracking by default _db.Orders.Update(order); await _db.SaveChangesAsync(); } // DELETE