aspnet-core

SKILL.md

ASP.NET Core Skill

ASP.NET Core Web API patterns for VanDaemon's backend. This project uses .NET 10 with thin controllers, singleton services, SignalR for real-time updates, and JSON file persistence. Controllers delegate to application services; business logic never lives in controllers.

Quick Start

Minimal Controller

[ApiController]
[Route("api/[controller]")]
public class TanksController : ControllerBase
{
    private readonly ITankService _tankService;

    public TanksController(ITankService tankService)
    {
        _tankService = tankService;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<Tank>>> GetAll(CancellationToken ct)
    {
        var tanks = await _tankService.GetAllTanksAsync(ct);
        return Ok(tanks);
    }

    [HttpPost("{id}/state")]
    public async Task<IActionResult> SetState(Guid id, [FromBody] object state, CancellationToken ct)
    {
        await _tankService.UpdateTankAsync(id, state, ct);
        return NoContent();
    }
}

Service Registration

// Program.cs - Register as singletons (VanDaemon pattern)
builder.Services.AddSingleton<ITankService, TankService>();
builder.Services.AddSingleton<IControlService, ControlService>();
builder.Services.AddSingleton<JsonFileStore>();

// SignalR
builder.Services.AddSignalR();

// After app.Build()
app.MapHub<TelemetryHub>("/hubs/telemetry");

Key Concepts

Concept Usage Example
Thin Controllers Delegate to services immediately return Ok(await _service.GetAsync(ct))
CancellationToken Accept in all async endpoints Task<T> Method(CancellationToken ct)
ActionResult Return type for typed responses ActionResult<List<Tank>>
Background Services Inherit BackgroundService TelemetryBackgroundService
Health Checks Map /health endpoint app.MapGet("/health", ...)

Common Patterns

Background Service with Scope

public class TelemetryBackgroundService : BackgroundService
{
    private readonly IServiceProvider _provider;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            using var scope = _provider.CreateScope();
            var tankService = scope.ServiceProvider.GetRequiredService<ITankService>();
            await tankService.RefreshAllAsync(ct);
            await Task.Delay(TimeSpan.FromSeconds(5), ct);
        }
    }
}

Health Endpoint

app.MapGet("/health", () => Results.Ok(new
{
    status = "healthy",
    timestamp = DateTime.UtcNow
}));

WARNING: Common Anti-Patterns

See patterns for detailed anti-pattern documentation including:

  • Business logic in controllers
  • Missing CancellationToken
  • Blocking calls in async context
  • Incorrect service lifetimes

See Also

  • patterns - Controller patterns, DI, anti-patterns
  • workflows - Adding endpoints, services, SignalR

Related Skills

  • See the csharp skill for language patterns and async/await
  • See the signalr skill for real-time hub implementation
  • See the serilog skill for structured logging
  • See the docker skill for containerized deployment
  • See the xunit skill for controller testing
Weekly Installs
1
GitHub Stars
1
First Seen
Mar 1, 2026
Installed on
amp1
cline1
opencode1
cursor1
continue1
kimi-cli1