用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/UitbreidenOS/UitKit --skill csharp命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Guidelines and instructions for Agent execution state rollback rules
Guidelines and instructions for Agent execution step counters limits
Guidelines and instructions for Agent execution timeout limits setups
基于 SOC 职业分类
正在显示 SKILL.md
| name | csharp |
| description | C#/.NET minimal API, Entity Framework Core, dependency injection, background services, middleware, LINQ |
IHostedService or BackgroundServiceMyApi/
├── MyApi.sln
├── src/
│ └── MyApi/
│ ├── Program.cs # Entry point + DI container
│ ├── appsettings.json
│ ├── appsettings.Development.json
│ ├── Controllers/ # Controller-based API
│ ├── Endpoints/ # Minimal API extensions
│ ├── Models/ # EF Core entities
│ ├── DTOs/ # Request/response shapes
│ ├── Services/ # Business logic interfaces + implementations
│ ├── Data/
│ │ └── AppDbContext.cs
│ └── Middleware/
└── tests/
└── MyApi.Tests/
// Program.cs — .NET 6+ top-level statements + minimal API
var builder = WebApplication.CreateBuilder(args);
// Register services
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
// Minimal API endpoints
app.MapGroup("/api/v1").MapUserEndpoints();
app.Run();
// Models/User.cs
public class User
{
public Guid Id { get; set; } = Guid.NewGuid();
public required string Email { get; set; }
public required string PasswordHash { get; set; }
public bool IsActive { get; set; } = true;
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
public ICollection<Post> Posts { get; set; } = [];
}
// Data/AppDbContext.cs
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
public DbSet<User> Users => Set<User>();
public DbSet<Post> Posts => Set<Post>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(e =>
{
e.HasIndex(u => u.Email).IsUnique();
e.Property(u => u.Email).HasMaxLength(320);
});
}
}
// Services/IUserService.cs
public interface IUserService
{
Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default);
Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default);
}
// Services/UserService.cs
public class UserService(AppDbContext db) : IUserService
{
public async Task<UserDto> GetByIdAsync(Guid id, CancellationToken ct = default)
{
var user = await db.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Id == id, ct)
?? throw new NotFoundException($"User {id} not found");
return new UserDto(user.Id, user.Email, user.CreatedAt);
}
public async Task<UserDto> CreateAsync(CreateUserRequest request, CancellationToken ct = default)
{
if (await db.Users.AnyAsync(u => u.Email == request.Email, ct))
throw new ConflictException("Email already in use");
var user = new User
{
Email = request.Email,
PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password)
};
db.Users.Add(user);
db.SaveChangesAsync(ct);
UserDto(user.Id, user.Email, user.CreatedAt);
}
}
[ApiController]
[Route("api/v1/[controller]")]
public class UsersController(IUserService userService) : ControllerBase
{
[HttpGet("{id:guid}")]
[ProducesResponseType<UserDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUser(Guid id, CancellationToken ct)
{
var user = await userService.GetByIdAsync(id, ct);
return Ok(user);
}
[HttpPost]
[ProducesResponseType<UserDto>(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
public async Task<IActionResult> CreateUser(
[FromBody] CreateUserRequest request,
CancellationToken ct)
{
var user = await userService.CreateAsync(request, ct);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
}
// Endpoints/UserEndpoints.cs
public static class UserEndpoints
{
public static RouteGroupBuilder MapUserEndpoints(this RouteGroupBuilder group)
{
var users = group.MapGroup("/users").WithTags("Users");
users.MapGet("/{id:guid}", async (Guid id, IUserService svc, CancellationToken ct) =>
{
var user = await svc.GetByIdAsync(id, ct);
return Results.Ok(user);
})
.WithName("GetUser")
.Produces<UserDto>();
return group;
}
}
// Middleware/RequestLoggingMiddleware.cs
public class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
{
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
try
{
await next(context);
}
finally
{
sw.Stop();
logger.LogInformation(
"{Method} {Path} {StatusCode} in {Elapsed}ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
sw.ElapsedMilliseconds);
}
}
}
// Register in Program.cs before other middleware:
app.UseMiddleware<RequestLoggingMiddleware>();
// Services/CleanupService.cs
public class CleanupService(IServiceProvider services, ILogger<CleanupService> logger)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await DoCleanupAsync(stoppingToken);
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
}
}
private async Task DoCleanupAsync(CancellationToken ct)
{
// Use a new scope for each iteration — BackgroundService is singleton
using var scope = services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var cutoff = DateTimeOffset.UtcNow.AddDays(-30);
await db.Sessions.Where(s => s.ExpiresAt < cutoff).ExecuteDeleteAsync(ct);
}
}
// Register: builder.Services.AddHostedService<CleanupService>();
// Always use AsNoTracking() for read-only queries
var users = await db.Users.AsNoTracking().Where(u => u.IsActive).ToListAsync(ct);
// Select only needed columns — avoid loading full entities for projections
var emails = await db.Users
.Where(u => u.IsActive)
.Select(u => u.Email)
.ToListAsync(ct);
// Use ExecuteUpdateAsync/ExecuteDeleteAsync for bulk ops — skips loading entities
await db.Users
.Where(u => !u.IsActive && u.CreatedAt < cutoff)
.ExecuteDeleteAsync(ct);
// Avoid N+1: use Include() for related data
var posts = await db.Posts
.Include(p => p.Author)
.Include(p => p.Tags)
.Where(p => p.Published)
.AsNoTracking()
.ToListAsync(ct);
// Use records for immutable DTOs
public record UserDto(Guid Id, string Email, DateTimeOffset CreatedAt);
public record CreateUserRequest(
[property: Required, EmailAddress] string Email,
[property: Required, MinLength(8)] string Password
);
// Response types with problem details (built-in .NET)
// Return Results.Problem() or throw exceptions caught by middleware
User: Add a BlogPost resource to a .NET Web API: CRUD endpoints, EF Core entity, migrations, and a background job that publishes scheduled posts.
Expected output:
Models/BlogPost.cs — entity with Id, Title, Body, AuthorId (FK to User), PublishedAt (nullable), ScheduledFor (nullable)DTOs/BlogPostDtos.cs — BlogPostDto record, CreateBlogPostRequest record with [Required] validationServices/IBlogPostService.cs + BlogPostService.cs — CRUD methods, GetPendingScheduledAsync for background jobControllers/BlogPostsController.cs — all CRUD with proper status codesServices/PostPublisherService.cs — BackgroundService that checks every minute and publishes due postsdotnet ef migrations add AddBlogPosts