Entity Framework Core fundamentals. DbContext configuration, entity configuration with IEntityTypeConfiguration, value converters, and connection resiliency. Trigger: EF Core, DbContext, entity configuration, database, ORM.
From dotnet-ai-kitnpx claudepluginhub faysilalshareef/dotnet-ai-kit --plugin dotnet-ai-kitThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
IEntityTypeConfiguration<T> for fluent configuration in separate filespublic sealed class AppDbContext(
DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<Product> Products => Set<Product>();
public DbSet<Customer> Customers => Set<Customer>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(AppDbContext).Assembly);
}
}
internal sealed class OrderConfiguration
: IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasKey(o => o.Id);
// Strongly-typed ID conversion
builder.Property(o => o.Id)
.HasConversion(
id => id.Value,
value => new OrderId(value));
builder.Property(o => o.CustomerName)
.HasMaxLength(200)
.IsRequired();
builder.Property(o => o.Status)
.HasConversion<string>()
.HasMaxLength(50);
// Row version for concurrency
builder.Property(o => o.RowVersion)
.IsRowVersion();
// Relationships
builder.HasMany(o => o.Items)
.WithOne()
.HasForeignKey(i => i.OrderId)
.OnDelete(DeleteBehavior.Cascade);
// Soft delete filter
builder.HasQueryFilter(o => !o.IsDeleted);
// Indexes
builder.HasIndex(o => o.CustomerName);
builder.HasIndex(o => o.CreatedAt);
}
}
// Strongly-typed ID converter
internal sealed class OrderIdConverter
: ValueConverter<OrderId, Guid>
{
public OrderIdConverter()
: base(id => id.Value, value => new OrderId(value)) { }
}
// Value object converter (owned type is preferred for complex VOs)
builder.OwnsOne(o => o.ShippingAddress, address =>
{
address.Property(a => a.Street).HasMaxLength(200);
address.Property(a => a.City).HasMaxLength(100);
address.Property(a => a.ZipCode).HasMaxLength(20);
});
builder.Services.AddDbContext<AppDbContext>((sp, options) =>
{
options.UseSqlServer(
builder.Configuration.GetConnectionString("Default"),
sql =>
{
sql.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(5),
errorNumbersToAdd: null);
sql.CommandTimeout(30);
sql.MigrationsAssembly(
typeof(AppDbContext).Assembly.FullName);
});
options.AddInterceptors(
sp.GetRequiredService<AuditableInterceptor>(),
sp.GetRequiredService<DomainEventInterceptor>());
});
// PostgreSQL alternative
options.UseNpgsql(connectionString, npgsql =>
{
npgsql.EnableRetryOnFailure(3);
npgsql.CommandTimeout(30);
});
public sealed class DomainEventInterceptor(IPublisher publisher)
: SaveChangesInterceptor
{
public override async ValueTask<int> SavedChangesAsync(
SaveChangesCompletedEventData eventData,
int result,
CancellationToken ct = default)
{
var context = eventData.Context!;
var aggregates = context.ChangeTracker
.Entries<AggregateRoot<object>>()
.Where(e => e.Entity.DomainEvents.Count != 0)
.Select(e => e.Entity)
.ToList();
foreach (var aggregate in aggregates)
{
foreach (var domainEvent in aggregate.DomainEvents)
await publisher.Publish(domainEvent, ct);
aggregate.ClearDomainEvents();
}
return result;
}
}
// For running migrations from CLI without the app host
internal sealed class AppDbContextFactory
: IDesignTimeDbContextFactory<AppDbContext>
{
public AppDbContext CreateDbContext(string[] args)
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.Development.json", optional: true)
.Build();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(config.GetConnectionString("Default"))
.Options;
return new AppDbContext(options);
}
}
OnModelCreating directly (use separate configuration files)IsRequired() / HasMaxLength() (relies on nullable annotations only)string for enums in the database without explicit conversion: DbContext class inheritanceUseSqlServer or UseNpgsql in Program.csIEntityTypeConfiguration<T> implementationsMigrations/ folderSaveChangesInterceptor implementationsApplyConfigurationsFromAssemblyIEntityTypeConfiguration<T>| Scenario | Approach |
|---|---|
| Strongly-typed ID | HasConversion with ValueConverter |
| Complex value object | OwnsOne owned entity |
| Soft delete | Global query filter |
| Concurrency | IsRowVersion() or IsConcurrencyToken() |
| Enum storage | HasConversion<string>() |