本文へ移動
cccskills
無料GitHub で公開

dotnet-aspnetcore-service

Use when building or extending a C# service on ASP.NET Core - Minimal APIs or controllers, DI and options, EF Core with Npgsql, ProblemDetails, HttpClientFactory resilience, async and cancellation

日本語の概要は準備中です。原文の説明を表示しています。

連携ツールPostgreSQL
インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.5 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

ASP.NET Core Service (C#)

Overview

ASP.NET Core is the .NET backend stack: one host for HTTP, DI, configuration, logging and background work. Build in layers so the framework stays at the edges and the domain stays plain C#.

Core principle: the repository's shape wins. Minimal APIs or MVC controllers, EF Core or Dapper, Clean Architecture projects or one project — follow what is there. The patterns below are for new code and for the gaps.

Layers

LayerResponsibilityASP.NET Core surface
EndpointHTTP in/out, binding, status codesMapGroup + MapPost (Minimal APIs) or [ApiController] controllers
Application serviceUse-case orchestration, business rulesa class registered in DI, constructor-injected, CancellationToken on every async method
DomainEntities, value objects, invariantsplain C#, no ASP.NET or EF attributes driving behaviour
InfrastructurePersistence, outbound HTTP, queuesDbContext, typed HttpClient, BackgroundService

Dependencies point inward: an endpoint calls a service, a service depends on an interface the infrastructure implements. An endpoint never contains business rules; a service never takes HttpContext.

Patterns

  • Target framework: keep the repository's TargetFramework. A new service targets the current LTS (net10.0); never move a repository to a preview SDK in a feature task. global.json pins the SDK when the repository has one.
  • Nullable reference types on (<Nullable>enable</Nullable>); treat a new nullable warning as a bug, not noise. Respect TreatWarningsAsErrors and .editorconfig analyzers.
  • Constructor injection (primary constructors are fine): public sealed class TaskService(ITaskRepository repo, TimeProvider clock). Lifetimes: DbContext scoped, stateless services scoped or singleton, never a scoped dependency captured by a singleton.
  • Options pattern for configuration: builder.Services.AddOptions<SmtpOptions>().BindConfiguration("Smtp").ValidateDataAnnotations().ValidateOnStart(); — never read IConfiguration["..."] in a service, never hardcode.
  • DTOs at the edge (records), mapped to and from the domain in the service. Never bind a request onto an EF entity or return one.
  • Validation at the boundary: DataAnnotations on the request record with builder.Services.AddValidation() (Minimal APIs, .NET 10) or [ApiController] model validation; FluentValidation where the repository already uses it. The domain enforces its own invariants too.
  • Errors in one shape: builder.Services.AddProblemDetails(), app.UseExceptionHandler() and an IExceptionHandler that maps domain exceptions to 400/404/409. Endpoints return TypedResults (Results<Created<TaskResponse>, ValidationProblem>), not ad-hoc anonymous objects. No stack trace in a response body.
  • Async all the way: await every I/O call and pass the request's CancellationToken down to EF and HttpClient. Never .Result, .Wait() or async void (outside event handlers).
  • EF Core: AsNoTracking() for reads, projection (Select) to DTOs instead of loading graphs, Include only for what the response needs. Postgres via Npgsql.EntityFrameworkCore.PostgreSQL. Raw SQL only through FromSql/SqlQuery with interpolated parameters (they are parameterized) — never FromSqlRaw with string concatenation.
  • Migrations: dotnet ef migrations add <Name>, review the generated Up/Down, commit the migration and the model snapshot together. Deploys apply an idempotent script (dotnet ef migrations script --idempotent) or a migration bundle; Database.Migrate() at startup races when several instances start and EnsureCreated() never belongs outside tests. postgres-migrations covers safe DDL.
  • Outbound HTTP: a typed client from IHttpClientFactory (builder.Services.AddHttpClient<PaymentsClient>()) with AddStandardResilienceHandler() (Microsoft.Extensions.Http.Resilience) for timeouts, retries and circuit breaking. Never new HttpClient() per call.
  • Background work: a BackgroundService that creates a scope per unit of work (IServiceScopeFactory) and honours stoppingToken.
  • Logging: ILogger<T> with message templates (logger.LogInformation("Task {TaskId} created", id)), never string interpolation in a log call; [LoggerMessage] source-generated methods on hot paths. No secrets or PII in a log or a ProblemDetails body.
  • Time and ids: inject TimeProvider instead of DateTime.UtcNow so tests control the clock; Guid.CreateVersion7() for new time-ordered ids where the repository has no id scheme.
  • OpenAPI: the built-in builder.Services.AddOpenApi() / app.MapOpenApi() document; keep it in step with api-contract-openapi. New code does not add Swashbuckle.

Worked Example

POST /tasks creating a task:

public sealed record CreateTaskRequest([property: Required, StringLength(200)] string Title);
public sealed record TaskResponse(Guid Id, string Title);

public static class TaskEndpoints
{
    public static RouteGroupBuilder MapTasks(this IEndpointRouteBuilder app)
    {
        var group = app.MapGroup("/tasks").RequireAuthorization();
        group.MapPost("/", Create);
        return group;
    }

    private static async Task<Created<TaskResponse>> Create(
        CreateTaskRequest req, TaskService service, CancellationToken ct)
    {
        var task = await service.CreateAsync(req.Title, ct);
        return TypedResults.Created($"/tasks/{task.Id}", new TaskResponse(task.Id, task.Title));
    }
}

public sealed class TaskService(ITaskRepository repo, TimeProvider clock)
{
    public async Task<TodoTask> CreateAsync(string title, CancellationToken ct)
    {
        var task = TodoTask.Create(title, clock.GetUtcNow()); // the domain enforces the 200-character invariant too
        await repo.AddAsync(task, ct);
        return task;
    }
}

The endpoint binds and validates; the service orchestrates; TodoTask.Create rejects an invalid title even when it is called from a job instead of HTTP.

Verify

Use the repository's own commands first (list_component_checks, the README, CI). Otherwise: dotnet build (warnings read, not skipped), dotnet format --verify-no-changes when the repository is formatted with it, and dotnet test. For the runtime check build once and run the output — dotnet publish <Api>.csproj -c Release -o /tmp/tt-<task key>/out, then ASPNETCORE_URLS=http://127.0.0.1:18080 dotnet /tmp/tt-<task key>/out/<Api>.dll in the background — rather than dotnet run, whose child process a terminal timeout can orphan.

Common Mistakes

  • Business logic in the endpoint or controller.
  • Returning an EF entity (lazy-loading surprises, leaked columns, cycles in the JSON).
  • .Result / .Wait() on a task — thread-pool starvation under load.
  • A CancellationToken accepted and never passed on.
  • new HttpClient() in a loop — socket exhaustion; IHttpClientFactory exists for this.
  • Applying migrations from every pod at startup.

Red Flags

  • using Microsoft.AspNetCore.* inside the domain project.
  • FromSqlRaw($"... {input}") or any SQL assembled from strings.
  • A singleton service with a DbContext constructor parameter.
  • A catch-all catch (Exception) that returns 200.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

Use when writing acceptance criteria for a task - express each as an observable Given/When/Then that QA can execute, including negative cases

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when the diff adds or changes an endpoint, resolver, RPC, job or query that takes an object id, a role check, a request binding or a tenant filter - BOLA/IDOR, function-level authorization, mass assignment and tenant scoping

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

Use on every UI change - semantic HTML, labels for controls, keyboard-navigable dialogs/menus, visible focus, and never color as the only signal

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when a task changes any screen, form, dialog, menu or control - Lighthouse/axe scan of the changed screens, a keyboard walk, and the thresholds that fail a task

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

How to work a task returned with review, QA or UAT findings. Use when a task is in need_revision or PR review comments are in your context.

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

Use when deciding whether a request needs an analiz task before implementation - the conditions that require the architect's analysis versus going straight to implementation

日本語の概要は準備中です。原文の説明を表示しています。

makifbaysal/tasktrooper1172026年10月18日 更新

makifbaysal のスキルをすべて見る

このスキルの問題を報告する