К основному содержанию
скилл CX Codex

dependency-injection — скилл для Claude Code: что делает и установка

Скилл внедряет паттерны dependency injection для .NET 10: выбор времён жизни сервисов, keyed services, декораторы через Scrutor и защиту от captive dependency.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add codewithmukesh/dotnet-claude-kit --skill dependency-injection -a claude-code
Открыть на GitHub

Что делает dependency-injection

Скилл закрепляет правила dependency injection для .NET 10. Конструктор остаётся единственным местом для передачи зависимостей. Никаких сервис-локаторов. Для выбора реализаций используются keyed services из .NET 8+ с атрибутом [FromKeyedServices].

Внутри собраны готовые фрагменты кода: декораторы через библиотеку Scrutor, регистрация через сканирование сборок и привязка секций конфигурации с помощью AddOptions. Захват scoped-сервисов вроде AppDbContext синглтонами приводит к утечкам памяти и устаревшим данным, поэтому для фоновых задач предписан IServiceScopeFactory.

Для проверки архитектуры автор рекомендует вызывать MCP-инструмент get_di_registrations. Он проверяет дубликаты регистраций и риски captive dependency за один вызов.

Какие задачи решает

  • настроить keyed services для выбора реализации по ключу
  • добавить декораторы к сервисам через библиотеку Scrutor
  • предотвратить captive dependency при использовании scoped DbContext в синглтонах
  • зарегистрировать классы по конвенциям через сканирование сборки
  • привязать типизированные настройки через AddOptions

Примеры запросов агенту

  • Помоги зарегистрировать keyed services для нескольких реализаций INotificationService
  • Как добавить декоратор логирования к IOrderService через Scrutor?
  • Исправь captive dependency: OrderCache зарегистрирован как singleton, но использует DbContext

Как установить dependency-injection

1Через Skills CLI (Vercel)

npx skills add codewithmukesh/dotnet-claude-kit --skill dependency-injection -a claude-code

2Вручную: положить файлы

~/.claude/skills/dependency-injection/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/dependency-injection/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о dependency-injection

Как установить dependency-injection в Claude Code?

Выполните команду npx skills add codewithmukesh/dotnet-claude-kit --skill dependency-injection -a claude-code.

Что делает скилл dependency-injection?

Он даёт агенту инструкции по правильной регистрации сервисов в .NET 10, настройке времён жизни объектов и подключению декораторов Scrutor.

Как скилл устраняет проблему captive dependency?

Вместо прямой передачи scoped-сервиса в singleton используется фабрика IServiceScopeFactory. Она создаёт отдельный AsyncServiceScope во время выполнения метода.

Исходный текст

SKILL.md · MIT
---
name: dependency-injection
description: >
  Dependency injection patterns for .NET 10. Covers service lifetimes, keyed
  services, the decorator pattern, factory pattern, and common DI pitfalls.
  Load this skill when registering services, resolving lifetime issues, designing
  service composition, or when the user mentions "DI", "dependency injection",
  "service registration", "AddScoped", "AddTransient", "AddSingleton", "keyed
  services", "decorator", "Scrutor", "IServiceCollection", or "captive dependency".
---

# Dependency Injection

## Core Principles

1. **Constructor injection is the default** — Inject dependencies through the constructor (primary constructors make this clean). No service locator, no property injection.
2. **Match lifetimes carefully** — A singleton must never depend on a scoped or transient service. This is the most common DI bug.
3. **Register interfaces, resolve interfaces** — Register `services.AddScoped<IOrderService, OrderService>()`, not the concrete type.
4. **Keyed services for strategy pattern** — .NET 8+ keyed services replace manual factory patterns for selecting between implementations.

## Patterns

### Keyed Services (.NET 8+)

Use keyed services to register and resolve multiple implementations of the same interface.

```csharp
// Registration
builder.Services.AddKeyedScoped<INotificationService, EmailNotificationService>("email");
builder.Services.AddKeyedScoped<INotificationService, SmsNotificationService>("sms");
builder.Services.AddKeyedScoped<INotificationService, PushNotificationService>("push");

// Resolution via attribute
public class OrderHandler([FromKeyedServices("email")] INotificationService notifier)
{
    public async Task Handle(CreateOrder.Command command, CancellationToken ct)
    {
        // ... create order
        await notifier.SendAsync(notification, ct);
    }
}

// Resolution via IServiceProvider
public class NotificationRouter(IServiceProvider provider)
{
    public INotificationService GetService(string channel)
    {
        return provider.GetRequiredKeyedService<INotificationService>(channel);
    }
}
```

### Decorator Pattern

```csharp
// Base service
public interface IOrderService
{
    Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct);
}

public class OrderService(AppDbContext db, TimeProvider clock) : IOrderService
{
    public async Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct)
    {
        var order = Order.Create(request, clock.GetUtcNow());
        db.Orders.Add(order);
        await db.SaveChangesAsync(ct);
        return Result.Success(order);
    }
}

// Decorator — adds logging
public class LoggingOrderService(IOrderService inner, ILogger<LoggingOrderService> logger) : IOrderService
{
    public async Task<Result<Order>> CreateAsync(CreateOrderRequest request, CancellationToken ct)
    {
        logger.LogInformation("Creating order for customer {CustomerId}", request.CustomerId);
        var result = await inner.CreateAsync(request, ct);
        if (result.IsSuccess)
            logger.LogInformation("Order {OrderId} created", result.Value.Id);
        return result;
    }
}

// Registration with Scrutor
builder.Services.AddScoped<IOrderService, OrderService>();
builder.Services.Decorate<IOrderService, LoggingOrderService>();
```

### Registration by Convention (Scrutor)

```csharp
// Auto-register all services matching a convention
builder.Services.Scan(scan => scan
    .FromAssemblyOf<Program>()
    .AddClasses(classes => classes.AssignableTo<ITransientService>())
    .AsImplementedInterfaces()
    .WithTransientLifetime()
    .AddClasses(classes => classes.AssignableTo<IScopedService>())
    .AsImplementedInterfaces()
    .WithScopedLifetime());
```

### Factory Pattern

When you need runtime logic to select an implementation.

```csharp
builder.Services.AddScoped<IPaymentProcessor>(sp =>
{
    var config = sp.GetRequiredService<IOptions<PaymentOptions>>().Value;
    return config.Provider switch
    {
        "stripe" => ActivatorUtilities.CreateInstance<StripeProcessor>(sp),
        "paypal" => ActivatorUtilities.CreateInstance<PayPalProcessor>(sp),
        _ => throw new InvalidOperationException($"Unknown payment provider: {config.Provider}")
    };
});
```

### Options Registration

```csharp
// Bind configuration section to a strongly-typed options class
builder.Services.AddOptions<JwtOptions>()
    .BindConfiguration("Jwt")
    .ValidateDataAnnotations()
    .ValidateOnStart();

// Inject as IOptions<T>
public class TokenService(IOptions<JwtOptions> options)
{
    private readonly JwtOptions _jwt = options.Value;
}
```

## Anti-patterns

### Don't Capture Scoped Services in Singletons

```csharp
// BAD — DbContext is scoped, captured by singleton = memory leak + stale data
builder.Services.AddSingleton<OrderCache>(); // depends on AppDbContext

// GOOD — use IServiceScopeFactory in singleton
public class OrderCache(IServiceScopeFactory scopeFactory)
{
    public async Task<Order?> GetAsync(Guid id)
    {
        await using var scope = scopeFactory.CreateAsyncScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        return await db.Orders.FindAsync(id);
    }
}
```

### Don't Register Everything as Singleton

```csharp
// BAD — making a service singleton when it holds mutable state
builder.Services.AddSingleton<OrderService>(); // has DbContext dependency

// GOOD — match the lifetime to the service's needs
builder.Services.AddScoped<OrderService>();
```

## Decision Guide

| Scenario | Recommendation |
|----------|---------------|
| Stateless service | Scoped (default) or Transient |
| Configuration / cache | Singleton |
| DbContext | Scoped (registered by `AddDbContext`) |
| Multiple implementations | Keyed services (strategy pattern) |
| Cross-cutting behavior | Decorator pattern |
| Convention-based registration | Scrutor |
| Runtime implementation selection | Factory delegate |
| Audit existing registrations | `get_di_registrations` MCP tool — lifetimes, duplicates, captive-dependency risks in one call |
| Strongly-typed config | `AddOptions<T>().BindConfiguration()` |

© codewithmukesh · MIT · codewithmukesh/dotnet-claude-kit

Похожие скиллы

Всё по теме «Бэкенд и API» →
dependency-injection-patterns Aaronontheweb Скилл учит раскладывать регистрации сервисов в ASP.NET Core по расширениям IServiceCollection, чтобы Program.cs не распухал. CX Codex +1 668установок microsoft-extensions-dependency-injection wshaddix Скилл для наведения порядка в регистрациях сервисов .NET: разбивает Program.cs на расширения и помогает выбрать время жизни сервиса. CX Codex +1 549установок golang-dependency-injection samber Помогает организовать внедрение зависимостей в Go-проектах: от ручных конструкторов до библиотек wire, dig, fx и do. CX Codex +1 39 500установок spring-boot-dependency-injection giuseppe-trisciuoglio Скилл внедряет паттерны внедрения зависимостей через конструктор в Spring Boot. Он помогает избавиться от field injection и настроить выбор бинов через @Primary и @Qualifier. CX Codex +1 3 800установок dotnet-best-practices официальный github Проверяет выделенный .NET/C# код по правилам проекта: от структуры и DI до тестов, логирования и безопасности. Нужен при ревью или доработке решения. 15 752установок migrate-static-to-wrapper dotnet Меняет статические вызовы в C# на инжектируемые абстракции: DateTime.UtcNow на TimeProvider, File.* на IFileSystem. Работает в границах одного файла, проекта или неймспейса. CX Codex +1 3 300установок

Из того же набора46

logging Скилл про наблюдаемость в .NET 10: разводит liveness и readiness, прокидывает correlation ID и задаёт уровни логов. CX Codex +1 2 800установок ci-cd Скилл помогает создавать YAML-пайплайны для проектов на .NET. Пригодится для быстрой настройки GitHub Actions и Azure DevOps с этапами сборки, тестов и публикации. CX Codex +1 2 800установок resilience Скилл для ИИ-агента, который подсказывает, как строить отказоустойчивые вызовы в .NET 10 на Polly v8: retry, circuit breaker, таймауты, fallback. CX Codex +1 2 800установок authentication Скилл для ИИ-агента, который подсказывает, как правильно настроить аутентификацию и авторизацию в ASP.NET Core: JWT, OIDC, Identity, политики. CX Codex +1 2 800установок minimal-api Скилл для ИИ-агентов, который ведёт по минимальным API .NET 10: группы эндпоинтов, TypedResults, фильтры и OpenAPI. CX Codex +1 2 800установок testing Скилл задаёт стратегию тестирования приложений .NET 10: интеграционные тесты через WebApplicationFactory, реальные базы в Testcontainers, снапшоты Verify. CX Codex +1 2 800установок ef-core Набор паттернов Entity Framework Core для .NET 10. Помогает настроить DbContext, пакетные обновления, миграции и проекции запросов без лишней нагрузки на базу данных. CX Codex +1 2 800установок error-handling Задаёт стратегию обработки ошибок в .NET 10: Result, ProblemDetails, валидация и глобальный перехват исключений. Нужен при проектировании контрактов API. CX Codex +1 2 800установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой