dotnet
.NET 9 / ASP.NET Core patterns with Minimal APIs, Clean Architecture, and EF Core. Trigger: When writing C# code, .NET APIs, or Entity Framework models.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
.NET 9 / ASP.NET Core patterns with Minimal APIs, Clean Architecture, and EF Core. Trigger: When writing C# code, .NET APIs, or Entity Framework models.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Implement tasks from the change, writing actual code following the specs and design. Trigger: When the orchestrator launches you to implement one or more tasks from a change.
Sync delta specs to main specs and archive a completed change. Trigger: When the orchestrator launches you to archive a change after implementation and verification.
Create technical design document with architecture decisions and approach. Trigger: When the orchestrator launches you to write or update the technical design for a change.
Explore and investigate ideas before committing to a change. Trigger: When the orchestrator launches you to think through a feature, investigate the codebase, or clarify requirements.
Initialize Spec-Driven Development context in any project. Detects stack, conventions, and bootstraps the active persistence backend. Trigger: When user wants to initialize SDD in a project, or says "sdd init", "iniciar sdd", "openspec init".
Create a change proposal with intent, scope, and approach. Trigger: When the orchestrator launches you to create or update a proposal for a change.
| name | dotnet |
| description | .NET 9 / ASP.NET Core patterns with Minimal APIs, Clean Architecture, and EF Core. Trigger: When writing C# code, .NET APIs, or Entity Framework models. |
| license | Apache-2.0 |
| metadata | {"author":"gentleman-programming","version":"1.0"} |
// ✅ Minimal API with typed results
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IOrderService, OrderService>();
var app = builder.Build();
var orders = app.MapGroup("/api/orders")
.WithTags("Orders")
.RequireAuthorization();
orders.MapGet("/", async (IOrderService service) =>
TypedResults.Ok(await service.GetAllAsync()));
orders.MapGet("/{id:int}", async (int id, IOrderService service) =>
await service.GetByIdAsync(id) is { } order
? TypedResults.Ok(order)
: TypedResults.NotFound());
orders.MapPost("/", async (CreateOrderDto dto, IOrderService service) =>
{
var order = await service.CreateAsync(dto);
return TypedResults.Created($"/api/orders/{order.Id}", order);
});
app.Run();
// ✅ Primary constructor for dependency injection
public class OrderService(
AppDbContext db,
ILogger<OrderService> logger,
IMapper mapper) : IOrderService
{
public async Task<List<OrderDto>> GetAllAsync()
{
logger.LogInformation("Fetching all orders");
var orders = await db.Orders.AsNoTracking().ToListAsync();
return mapper.Map<List<OrderDto>>(orders);
}
}
// ❌ NEVER: Manual field assignment from constructor
public class OrderService
{
private readonly AppDbContext _db;
public OrderService(AppDbContext db) { _db = db; }
}
src/
├── Domain/ # Entities, value objects, interfaces
│ ├── Entities/
│ ├── Interfaces/ # IOrderRepository, IUnitOfWork
│ └── ValueObjects/
├── Application/ # Use cases, DTOs, validators
│ ├── Orders/
│ │ ├── Commands/ # CreateOrderCommand, handler
│ │ ├── Queries/ # GetOrderQuery, handler
│ │ └── Dtos/
│ └── Common/ # Behaviors, mappings
├── Infrastructure/ # EF Core, external services
│ ├── Persistence/ # DbContext, configs, migrations
│ ├── Services/ # Email, storage, etc.
│ └── DependencyInjection.cs
└── WebApi/ # Minimal API endpoints, middleware
├── Endpoints/
└── Program.cs
// ✅ Entity configuration (Fluent API, NOT data annotations)
public class OrderConfiguration : IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Total).HasPrecision(18, 2);
builder.HasMany(o => o.Items)
.WithOne(i => i.Order)
.HasForeignKey(i => i.OrderId)
.OnDelete(DeleteBehavior.Cascade);
builder.HasIndex(o => o.CreatedAt);
}
}
// ✅ DbContext with configuration scanning
public class AppDbContext(DbContextOptions<AppDbContext> options)
: DbContext(options)
{
public DbSet<Order> Orders => Set<Order>();
public DbSet<Product> Products => Set<Product>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(
typeof(AppDbContext).Assembly);
}
}
// ✅ Generic repository only if you need abstraction over EF
public interface IRepository<T> where T : class
{
Task<T?> GetByIdAsync(int id);
Task<List<T>> GetAllAsync();
void Add(T entity);
void Remove(T entity);
}
// ✅ Prefer using DbContext directly for simple cases
// Repositories add value only when you need to swap persistence
// ✅ Records for DTOs (immutable, concise)
public record CreateOrderDto(string CustomerName, List<OrderItemDto> Items);
public record OrderDto(int Id, string CustomerName, decimal Total, DateTime CreatedAt);
public record OrderItemDto(int ProductId, int Quantity);
// ❌ NEVER: Expose domain entities in API responses
// ✅ Result type for expected failures
public class Result<T>
{
public T? Value { get; }
public string? Error { get; }
public bool IsSuccess => Error is null;
public static Result<T> Success(T value) => new() { Value = value };
public static Result<T> Failure(string error) => new() { Error = error };
}
// ❌ NEVER: throw for business logic (e.g., "order not found")
// ✅ Extension methods per layer
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(
this IServiceCollection services, IConfiguration config)
{
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(config.GetConnectionString("Default")));
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
return services;
}
}
// In Program.cs:
builder.Services.AddInfrastructure(builder.Configuration);
// ✅ Problem Details with IExceptionHandler (.NET 8+)
public class GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
: IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(
HttpContext context, Exception exception, CancellationToken ct)
{
logger.LogError(exception, "Unhandled exception");
var problem = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "Server Error",
};
context.Response.StatusCode = problem.Status.Value;
await context.Response.WriteAsJsonAsync(problem, ct);
return true;
}
}
dotnet, .net, asp.net, c#, csharp, minimal api, ef core, entity framework, clean architecture, dependency injection