CommunityCodierung & Entwicklunggithub.com

Dependency-Injection-Patterns

Organisieren Sie DI-Registrierungen mit IServiceCollection-Erweiterungsmethoden. Gruppieren Sie zusammengehörige Dienste in zusammensetzbare Add*-Methoden für eine saubere Program.cs und wiederverwendbare Konfiguration in Tests.

Was ist Dependency-Injection-Patterns?

Diese Fähigkeit (Skill) für KI-Agenten beschreibt das Dependency-Injection-Pattern unter Verwendung von IServiceCollection-Erweiterungsmethoden in .NET-Anwendungen. Sie adressiert das Problem unübersichtlicher Program.cs-Dateien, indem sie eine Methode zur Gruppierung von Service-Registrierungen in separaten, benannten Erweiterungsmethoden (z.B. `AddUserServices()`, `AddOrderServices()`) bereitstellt. Der Skill unterstützt Microsoft.Extensions.DependencyInjection, ASP.NET Core, Akka.Hosting und Testumgebungen wie WebApplicationFactory. Er erklärt die grundlegende Struktur der Erweiterungsmethoden mit und ohne Konfigurationsparametern, beschreibt bewährte Namenskonventionen, Dateiorganisation und Anti-Patterns. Zudem behandelt er die Lebensdauerverwaltung (Singleton, Scoped, Transient) und häufige Fehler wie das Injizieren von Scoped-Diensten in Singletons oder das Fehlen von Scopes in Hintergrunddiensten. Die enthaltene Referenzdatei 'advanced-patterns.md' vertieft Themen wie Tests mit DI-Erweiterungen, Akka.NET-Actor-Scope-Management und bedingte/factory-/keyed-Registrierungen.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/microsoft-extensions-dependency-injection

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Was macht Dependency-Injection-Patterns?

Wann dieser Skill verwendet werden sollte

Verwenden Sie diesen Skill, wenn:

  • Sie Service-Registrierungen in ASP.NET Core-Anwendungen organisieren möchten
  • Sie übermäßig große Program.cs/Startup.cs-Dateien mit hunderten von Registrierungen vermeiden wollen
  • Sie die Service-Konfiguration zwischen Produktion und Tests wiederverwendbar machen möchten
  • Sie Bibliotheken entwerfen, die in Microsoft.Extensions.DependencyInjection integriert werden sollen

Referenzdatei

  • advanced-patterns.md: Testen mit DI-Erweiterungen, Akka.NET-Actor-Scope-Management, bedingte/factory-/keyed-Registrierungsmuster

Das Problem

Ohne Organisation wird Program.cs unüberschaubar:

// SCHLECHT: Über 200 Zeilen unorganisierter Registrierungen
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IUserService, UserService>();
// ... 150 weitere Zeilen ...

Probleme: Zusammengehörige Registrierungen sind schwer zu finden, keine klaren Grenzen, keine Wiederverwendung in Tests, Merge-Konflikte.


Die Lösung: Erweiterungsmethoden-Komposition

Gruppieren Sie zusammengehörige Registrierungen in Erweiterungsmethoden:

// GUT: Saubere, zusammensetzbare Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddUserServices()
    .AddOrderServices()
    .AddEmailServices()
    .AddPaymentServices()
    .AddValidators();

var app = builder.Build();

Das Erweiterungsmethoden-Muster

Grundlegende Struktur

namespace MyApp.Users;

public static class UserServiceCollectionExtensions
{
    public static IServiceCollection AddUserServices(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserReadStore, UserReadStore>();
        services.AddScoped<IUserWriteStore, UserWriteStore>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IUserValidationService, UserValidationService>();

        return services;
    }
}

Mit Konfiguration

namespace MyApp.Email;

public static class EmailServiceCollectionExtensions
{
    public static IServiceCollection AddEmailServices(
        this IServiceCollection services,
        string configSectionName = "EmailSettings")
    {
        services.AddOptions<EmailOptions>()
            .BindConfiguration(configSectionName)
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
        services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
        services.AddScoped<IUserEmailComposer, UserEmailComposer>();
        services.AddScoped<IEmailSender, SmtpEmailSender>();

        return services;
    }
}

Dateiorganisation

Platzieren Sie Erweiterungsmethoden in der Nähe der Dienste, die sie registrieren:

src/
  MyApp.Api/
    Program.cs                    # Setzt alle Add*-Methoden zusammen
  MyApp.Users/
    Services/
      UserService.cs
    UserServiceCollectionExtensions.cs   # AddUserServices()
  MyApp.Orders/
    OrderServiceCollectionExtensions.cs  # AddOrderServices()
  MyApp.Email/
    EmailServiceCollectionExtensions.cs  # AddEmailServices()

Konvention: {Feature}ServiceCollectionExtensions.cs neben den Diensten des Features.


Namenskonventionen

MusterVerwendungszweck
Add{Feature}Services()Allgemeine Feature-Registrierung
Add{Feature}()Kurzform, wenn eindeutig
Configure{Feature}()Wenn hauptsächlich Optionen gesetzt werden
Use{Feature}()Middleware (auf IApplicationBuilder)

Vorteile für Tests

Das Add*-Muster ermöglicht es Ihnen, die Produktionskonfiguration in Tests wiederzuverwenden und nur das zu überschreiben, was anders ist. Funktioniert mit WebApplicationFactory, Akka.Hosting.TestKit und eigenständigen ServiceCollection.

Vollständige Testbeispiele finden Sie in advanced-patterns.md.


Geschichtete Erweiterungen

Bei größeren Anwendungen setzen Sie Erweiterungen hierarchisch zusammen:

public static class AppServiceCollectionExtensions
{
    public static IServiceCollection AddAppServices(this IServiceCollection services)
    {
        return services
            .AddDomainServices()
            .AddInfrastructureServices()
            .AddApiServices();
    }
}

public static class DomainServiceCollectionExtensions
{
    public static IServiceCollection AddDomainServices(this IServiceCollection services)
    {
        return services
            .AddUserServices()
            .AddOrderServices()
            .AddProductServices();
    }
}

Integration in Akka.Hosting

Das gleiche Muster funktioniert für die Akka.NET-Actor-Konfiguration:

public static class OrderActorExtensions
{
    public static AkkaConfigurationBuilder AddOrderActors(
        this AkkaConfigurationBuilder builder)
    {
        return builder
            .WithActors((system, registry, resolver) =>
            {
                var orderProps = resolver.Props<OrderActor>();
                var orderRef = system.ActorOf(orderProps, "orders");
                registry.Register<OrderActor>(orderRef);
            });
    }
}

// Verwendung in Program.cs
builder.Services.AddAkka("MySystem", (builder, sp) =>
{
    builder
        .AddOrderActors()
        .AddInventoryActors()
        .AddNotificationActors();
});

Vollständige Akka.Hosting-Muster finden Sie im Skill akka-hosting-actor-patterns.


Anti-Patterns

Nicht: Alles in Program.cs registrieren

// SCHLECHT: Riesige Program.cs mit über 200 Zeilen Registrierungen

Nicht: Zu allgemeine Erweiterungen erstellen

// SCHLECHT: Zu vage, kommuniziert nicht, was registriert wird
public static IServiceCollection AddServices(this IServiceCollection services) { ... }

Nicht: Wichtige Konfiguration verstecken

// SCHLECHT: Versteckte Einstellungen
public static IServiceCollection AddDatabase(this IServiceCollection services)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer("hartcodierte-verbindungszeichenfolge"));  // Versteckt!
}

// GUT: Konfiguration explizit akzeptieren
public static IServiceCollection AddDatabase(
    this IServiceCollection services,
    string connectionString)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(connectionString));
}

Zusammenfassung der Best Practices

PraxisNutzen
Gruppieren zusammengehöriger Dienste in Add*-MethodenSaubere Program.cs, klare Grenzen
Platzieren von Erweiterungen nahe der registrierten DiensteLeicht zu finden und zu warten
Rückgabe von IServiceCollection für MethodenverkettungFlüssige API
Akzeptieren von KonfigurationsparameternFlexibilität
Konsistente Namensgebung (Add{Feature}Services)Auffindbarkeit
Testen durch Wiederverwendung von ProduktionserweiterungenVertrauen, weniger Duplikate

Lebensdauerverwaltung

LebensdauerVerwenden, wennBeispiele
SingletonZustandslos, thread-sicher, teuer zu erstellenKonfiguration, HttpClient-Fabriken, Caches
ScopedZustandsbehaftet pro Anfrage, DatenbankkontexteDbContext, Repositories, Benutzerkontext
TransientLeichtgewichtig, zustandsbehaftet, günstig zu erstellenValidatoren, kurzlebige Helfer
// SINGLETON: Zustandslose Dienste, sicher geteilt
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();

// SCOPED: Datenbankzugriff, Zustand pro Anfrage
services.AddScoped<IUserRepository, UserRepository>();

// TRANSIENT: Günstig, kurzlebig
services.AddTransient<CreateUserRequestValidator>();

Scoped-Dienste benötigen einen Scope. ASP.NET Core erstellt einen pro HTTP-Anfrage. In Hintergrunddiensten und Akteuren erstellen Sie Scopes manuell.

Scope-Management-Muster für Akteure finden Sie in advanced-patterns.md.


Häufige Fehler

Injizieren von Scoped-Diensten in Singletons

// SCHLECHT: Singleton erfasst Scoped-Dienst - veralteter DbContext!
public class CacheService  // Als Singleton registriert
{
    private readonly IUserRepository _repo;  // Scoped - beim Start erfasst!
}

// GUT: IServiceProvider injizieren, pro Operation einen Scope erstellen
public class CacheService
{
    private readonly IServiceProvider _serviceProvider;

    public async Task<User> GetUserAsync(string id)
    {
        using var scope = _serviceProvider.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.GetByIdAsync(id);
    }
}

Kein Scope in Hintergrundaufgaben

// SCHLECHT: Kein Scope für Scoped-Dienste
public class BadBackgroundService : BackgroundService
{
    private readonly IOrderService _orderService;  // Scoped - wird einen Fehler auslösen!
}

// GUT: Für jede Arbeitseinheit einen Scope erstellen
public class GoodBackgroundService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
        // ...
    }
}

Ressourcen

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

Verwandte Skills