coder-csharp-efcore-config
npx skills add https://github.com/ozerohax/assistagents --skill coder-csharp-efcore-config
Agent 安装分布
Skill 文档
<skill_overview> Properly configure EF Core entities, relationships, and DbContext Creating new entities or DbContext Configuring entity relationships Setting up database schema Managing migrations Configuring value converters or query filters Microsoft EF Core Documentation Creating and Configuring a Model </skill_overview> <migrations_policy> NEVER create or edit migration files manually. Always use CLI or Package Manager Console. <allowed_commands> dotnet ef migrations add MigrationName dotnet ef database update dotnet ef migrations remove Add-Migration MigrationName Update-Database Remove-Migration </allowed_commands> Manually creating migration .cs files Manually editing Up()/Down() methods Using EnsureCreated() in production
<naming_convention> Use descriptive migration names AddUserEmailIndex, CreateOrdersTable, AddSoftDeleteToProducts Migration1, Update, Fix, Changes </naming_convention>
<ci_cd_approach> Apply migrations on application startup for simple deployments // Program.cs var app = builder.Build(); // Apply pending migrations using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService<AppDbContext>(); db.Database.Migrate(); } app.Run(); <production_alternative> Generate SQL script for production deployments dotnet ef migrations script –idempotent -o migrate.sql </production_alternative> </ci_cd_approach>
// Properties
builder.Property(u => u.Email)
.IsRequired()
.HasMaxLength(256);
builder.Property(u => u.DisplayName)
.HasMaxLength(100);
builder.Property(u => u.CreatedAt)
.HasDefaultValueSql("GETUTCDATE()");
// Indexes
builder.HasIndex(u => u.Email)
.IsUnique();
// Relationships
builder.HasMany(u => u.Orders)
.WithOne(o => o.User)
.HasForeignKey(o => o.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
} // DbContext public class AppDbContext : DbContext { public DbSet<User> Users => Set<User>(); public DbSet<Order> Orders => Set<Order>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Apply all configurations from assembly
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(AppDbContext).Assembly);
}
}
<context_structure> public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
// DbSets
public DbSet<User> Users => Set<User>();
public DbSet<Order> Orders => Set<Order>();
public DbSet<Product> Products => Set<Product>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Apply configurations
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
// Global query filters
modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDeleted);
modelBuilder.Entity<Order>().HasQueryFilter(o => !o.IsDeleted);
}
// Override SaveChanges for audit fields
public override Task<int> SaveChangesAsync(CancellationToken ct = default)
{
foreach (var entry in ChangeTracker.Entries<IAuditable>())
{
if (entry.State == EntityState.Added)
entry.Entity.CreatedAt = DateTime.UtcNow;
if (entry.State == EntityState.Modified)
entry.Entity.UpdatedAt = DateTime.UtcNow;
}
return base.SaveChangesAsync(ct);
}
} </context_structure> </dbcontext_setup> <one_to_many> // User has many Orders builder.HasMany(u => u.Orders) .WithOne(o => o.User) .HasForeignKey(o => o.UserId) .OnDelete(DeleteBehavior.Cascade); </one_to_many>
<many_to_many> // Product <-> Category (with explicit join entity) builder.HasMany(p => p.Categories) .WithMany(c => c.Products) .UsingEntity<ProductCategory>( j => j.HasOne(pc => pc.Category) .WithMany() .HasForeignKey(pc => pc.CategoryId), j => j.HasOne(pc => pc.Product) .WithMany() .HasForeignKey(pc => pc.ProductId)); </many_to_many>
<one_to_one> // User has one Profile builder.HasOne(u => u.Profile) .WithOne(p => p.User) .HasForeignKey<UserProfile>(p => p.UserId); </one_to_one> <global_query_filters> Automatically apply filters to all queries <use_cases> <use_case name=”soft_delete”> modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDeleted); modelBuilder.Entity<Order>().HasQueryFilter(o => !o.IsDeleted); </use_case> <use_case name=”multi_tenancy”> // Inject tenant ID via DbContext constructor public class AppDbContext : DbContext { private readonly string _tenantId;
public AppDbContext(DbContextOptions options, ITenantService tenant)
: base(options)
{
_tenantId = tenant.CurrentTenantId;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasQueryFilter(o => o.TenantId == _tenantId);
}
} </use_case> </use_cases> // When you need unfiltered data var allUsers = await context.Users .IgnoreQueryFilters() .ToListAsync(); </global_query_filters> <value_converters> Convert between .NET types and database types builder.Property(o => o.Status) .HasConversion<string>() .HasMaxLength(20); builder.Property(u => u.Tags) .HasConversion( v => string.Join(‘,’, v), v => v.Split(‘,’, StringSplitOptions.RemoveEmptyEntries).ToList()); builder.Property(u => u.SocialSecurityNumber) .HasConversion( v => _encryptor.Encrypt(v), v => _encryptor.Decrypt(v)); </value_converters> <json_columns> Store complex objects as JSON (EF Core 7+) public class Author { public int Id { get; set; } public string Name { get; set; } public ContactInfo Contact { get; set; } // Stored as JSON } public class ContactInfo { public string Email { get; set; } public string Phone { get; set; } public Address Address { get; set; } } // Configuration builder.OwnsOne(a => a.Contact, contact => { contact.ToJson(); contact.OwnsOne(c => c.Address); }); // Query JSON properties var authors = await context.Authors .Where(a => a.Contact.Address.City == “London”) .ToListAsync(); </json_columns> <entity_design_guidelines> public abstract class BaseEntity { public int Id { get; set; } public DateTime CreatedAt { get; set; } public DateTime? UpdatedAt { get; set; } } public abstract class SoftDeletableEntity : BaseEntity { public bool IsDeleted { get; set; } public DateTime? DeletedAt { get; set; } }
// FK property
public int UserId { get; set; }
// Navigation property
public User User { get; set; }
} </entity_design_guidelines>