Claude-skill-registry dotnet-efcore-guidelines
Entity Framework Core best practices for Clean Architecture projects. Covers DbContext, entity configurations, repository pattern, migrations, and PostgreSQL-specific features.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dotnet-efcore-guidelines" ~/.claude/skills/majiayu000-claude-skill-registry-dotnet-efcore-guidelines && rm -rf "$T"
skills/data/dotnet-efcore-guidelines/SKILL.md.NET + Entity Framework Core Guidelines
Project-Agnostic EF Core Patterns
Placeholders use
syntax - see docs/TEMPLATE_GLOSSARY.md.{Placeholder}
Purpose
This skill provides comprehensive best practices for using Entity Framework Core with PostgreSQL in Clean Architecture projects. It details conventions for DbContext, entity configurations, repository patterns, migrations, and PostgreSQL-specific features.
When This Skill Activates
Triggered by:
- Keywords: "ef core", "entity framework", "dbcontext", "repository", "migration", "database", "postgres", "postgresql"
- File patterns:
,**/Persistence/**/*.cs
,**/Repositories/**/*.cs
,**/*DbContext.cs**/Configurations/**/*.cs
EF Core Architecture
The persistence layer (
{Project}.Persistence) is responsible for data access and storage. It implements interfaces defined in the Application layer, adhering to Clean Architecture principles.
graph TD subgraph Application Layer A[I{Entity}Repository] --> B[{Entity}] B[{Entity}] --> C[Domain Layer] end subgraph Persistence Layer D[{DbContext}] --> B E[{Entity}Repository] --> D E --> A end A -- Implemented by --> E C -- Used by --> B D -- Configures --> B
Resources
For more detailed examples, refer to the
folder within this skill.resources/
| Resource | Description |
|---|---|
| dbcontext-patterns.md | DbContext configuration, override |
| entity-configuration.md | , TPT, PostgreSQL functions |
| repository-pattern.md | , custom repositories |
| querying-patterns.md | , , projections, performance |
| migrations.md | Creating and applying migrations |
Quick Reference
1. DbContext Pattern
The
{DbContext} manages database interactions.
// File: {Project}.Persistence/{DbContext}.cs public class {DbContext} : DbContext { public {DbContext}(DbContextOptions<{DbContext}> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); // Automatically applies all IEntityTypeConfiguration<T> from the assembly modelBuilder.ApplyConfigurationsFromAssembly(typeof({DbContext}).Assembly); } // Override SaveChangesAsync for cross-cutting concerns like auditing or soft deletes public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default) { // Example: Audit logging or automatic timestamp updates foreach (var entry in ChangeTracker.Entries()) { // Handle CreatedAt, UpdatedAt logic } return base.SaveChangesAsync(cancellationToken); } public DbSet<{Entity}> {Entities} { get; set; } = null!; public DbSet<{ParentEntity}> {ParentEntities} { get; set; } = null!; // ... other DbSets }
For more details, see dbcontext-patterns.md.
2. Entity Configuration
All entity-specific configurations are done using
IEntityTypeConfiguration<T> in separate classes.
// File: {Project}.Persistence/Configurations/Entities/{Entity}Configuration.cs public class {Entity}Configuration : IEntityTypeConfiguration<{Entity}> { public void Configure(EntityTypeBuilder<{Entity}> builder) { // Project Standard: Table Per Type (TPT) inheritance strategy builder.UseTptMappingStrategy(); // Project Standard: UUIDv7 primary keys for main entities builder.Property(e => e.Id).HasDefaultValueSql("uuidv7()"); // Database-level defaults are acceptable here, not in domain entities builder.Property(e => e.ViewCount).HasDefaultValue(0); builder.Property(e => e.Title).HasMaxLength(200).IsRequired(); builder.Property(e => e.Description).HasMaxLength(5000); // Example relationship configuration builder.HasOne(e => e.{ParentEntity}) .WithMany() .HasForeignKey(e => e.{ParentEntity}Id) .OnDelete(DeleteBehavior.Restrict); } }
For more details, see entity-configuration.md.
3. Repository Pattern
Repositories abstract data access. Interfaces reside in the Application layer, and implementations are in the Persistence layer.
CRITICAL RULE: Repositories MUST return DOMAIN ENTITIES, not DTOs. DTO mapping always happens in the Application layer handlers via AutoMapper.
// File: {Project}.Application/Contracts/Persistence/I{Entity}Repository.cs (Application Layer) public interface I{Entity}Repository : IGenericRepository<{Entity}, {IdType}> { Task<List<{Entity}>> Get{Entities}WithDetails(); // Returns List<{Entity}> Task<{Entity}?> Get{Entity}WithDetails({IdType} id); // Returns {Entity}? } // File: {Project}.Persistence/Repositories/{Entity}Repository.cs (Persistence Layer) public class {Entity}Repository : GenericRepository<{Entity}, {IdType}>, I{Entity}Repository { private readonly {DbContext} _dbContext; public {Entity}Repository({DbContext} dbContext) : base(dbContext) => _dbContext = dbContext; public async Task<List<{Entity}>> Get{Entities}WithDetails() { return await _dbContext.{Entities} .Include(e => e.{LookupEntity}) .Include(e => e.{RelatedEntity1}) .Include(e => e.{RelatedEntity2}) .ToListAsync(); // Returns entities } }
For more details, see repository-pattern.md.
4. Querying Patterns
Efficient querying is crucial for performance. Avoid N+1 issues by using eager loading (
Include) and projections (Select).
// Example: Query with eager loading and projection to DTO (in Application Layer Handler) public async Task<List<{Entity}ListDto>> Handle(Get{Entity}ListRequest request, CancellationToken cancellationToken) { var {entities} = await _{entity}Repository.Get{Entities}WithDetails(); // Repository returns entities return _mapper.Map<List<{Entity}ListDto>>({entities}); // Handler maps to DTOs } // Example: Using AsNoTracking for read-only queries (in Repository) public async Task<List<{Entity}>> Get{Entities}ReadOnly() { return await _dbContext.{Entities} .AsNoTracking() // Disables change tracking for performance .Include(e => e.{ParentEntity}) .ToListAsync(); }
For more details, see querying-patterns.md.
5. Migrations
Database schema changes are managed through EF Core migrations.
# Create a new migration for schema changes dotnet ef migrations add AddNewFieldTo{Entity} --project {Project}.Persistence # Apply pending migrations to the database dotnet ef database update --project {Project}.Persistence # Generate SQL script for production deployment dotnet ef migrations script --idempotent --output migrations/release.sql --project {Project}.Persistence
For more details, see migrations.md.
Key Principles & Conventions
- IDs: All primary keys are
(orGuid
), except for lookup tables which use{IdType}
(orint
).{LookupIdType} - Numeric Types: Use
instead ofint
unless explicitly required for large values (e.g., file sizes, pagination cursors).long - Default Values: DO NOT add default values in domain entity property initializers (e.g.,
). Set defaults in application handlers or use database-level defaults viapublic int ViewCount { get; set; } = 0;
.IEntityTypeConfiguration - Link Tables: Navigation properties on link/mapping tables are readonly for queries only. Writes must go through the link table's repository directly.
- PostgreSQL Features: Leverage PostgreSQL-specific features like
for primary keys and PostGIS for spatial data handling.UUIDv7
Related Documentation:
- Conceptual domain model.docs/DOMAIN.md
- Overall system architecture.docs/ARCHITECTURE.md
- Dependency enforcement.clean-architecture-rules