Введение:
В мире enterprise-разработки на C# Dependency Injection давно перестал быть опциональным паттерном — это обязательный стандарт построения масштабируемых и поддерживаемых приложений. ASP.NET Core с самого начала был спроектирован с DI в ядре фреймворка, что кардинально отличает его от классической ASP.NET.
Почему DI стал критически важным?
-
Сложность современных систем: микросервисная архитектура требует четкого разделения ответственности
-
Тестируемость: только DI позволяет легко заменять реализации на моки в unit-тестах
-
Гибкость: замена компонентов без переписывания кода всей системы
-
Производительность: правильное управление временем жизни объектов снижает нагрузку на память
Что делает DI в ASP.NET Core особенным?
-
Встроенный контейнер: не требуются сторонние библиотеки для базовых сценариев
-
Интеграция со всеми компонентами: от контроллеров до middleware и фоновых служб
-
Глубокая оптимизация: разработан specifically для высоконагруженных web-приложений
-
Простота и мощность: минимальный код для старта, но неограниченные возможности для роста
1. Базовая регистрация сервисов
1.1. Регистрация в Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IUserService, UserService>();
services.AddSingleton<ILogger, FileLogger>();
services.AddTransient<IEmailSender, EmailSender>();
}
1.2. Внедрение в контроллер
[ApiController]
[Route(«api/users»)]
public class UserController : ControllerBase
{
private readonly IUserService _userService;public UserController(IUserService userService)
{
_userService = userService;
}[HttpGet(«{id}»)]
public async Task<IActionResult> GetUser(int id)
{
var user = await _userService.GetUserAsync(id);
return Ok(user);
}
}
2. Время жизни сервисов
2.1. Transient — новая копия каждый раз
services.AddTransient<ITransientService, TransientService>();
2.2. Scoped — одна копия на запрос
services.AddScoped<IScopedService, ScopedService>();
2.3. Singleton — одна копия на всё приложение
services.AddSingleton<ISingletonService, SingletonService>();
3. Фабрики и опции
3.1. Фабрика сервисов
services.Configure<DatabaseOptions>(Configuration.GetSection(«Database»));
3.3. Внедрение IOptions
public class UserService
{
private readonly DatabaseOptions _options;public UserService(IOptions<DatabaseOptions> options)
{
_options = options.Value;
}
}
4. Интеграция с Entity Framework
4.1. Регистрация DbContext
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString(«Default»)));
4.2. Использование в репозитории
public class UserRepository : IUserRepository
{
private readonly ApplicationDbContext _context;public UserRepository(ApplicationDbContext context)
{
_context = context;
}public async Task<User> GetUserAsync(int id)
{
return await _context.Users.FindAsync(id);
}
}
5. Ошибки и лучшие практики
5.1. Captive Dependency — неправильное время жизни
// ОШИБКА: Singleton зависит от Scoped
services.AddSingleton<ISingletonService>(provider =>
{
var scopedService = provider.GetService<IScopedService>(); // Захват Scoped в Singleton!
return new SingletonService(scopedService);
});
5.2. Правильное решение
services.AddSingleton<ISingletonService>(provider =>
{
using var scope = provider.CreateScope();
var scopedService = scope.ServiceProvider.GetService<IScopedService>();
return new SingletonService(scopedService);
});
5.3. Внедрение в Middleware
public class CustomMiddleware
{
private readonly RequestDelegate _next;
private readonly IService _service;public CustomMiddleware(RequestDelegate next, IService service)
{
_next = next;
_service = service;
}public async Task InvokeAsync(HttpContext context)
{
await _service.DoSomethingAsync();
await _next(context);
}
}
6. Продвинутые сценарии
6.1. Generic-сервисы
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
6.2. Группировка регистраций
services.AddInfrastructureServices()
.AddApplicationServices()
.AddDomainServices();
6.3. Валидация зависимостей
var serviceProvider = services.BuildServiceProvider();
serviceProvider.ValidateScopes(); // Проверка в Development
Заключение
Dependency Injection в ASP.NET Core — это не просто технология, а философия построения современных приложений. Мы рассмотрели ключевые аспекты:
-
Упрощение архитектуры через инверсию управления и уменьшение связанности компонентов
-
Контроль времени жизни объектов (Transient, Scoped, Singleton) для оптимизации памяти
-
Глубокая интеграция с Entity Framework, Middleware и другими компонентами фреймворка
-
Повышение тестируемости за счет легкого внедрения моков и заглушек
-
Избежание распространенных ошибок (Captive Dependency, неправильная регистрация)
Освоив DI, вы не просто научились использовать контейнер зависимостей — вы начали мыслить в терминах слабосвязанной архитектуры, что является обязательным навыком для современного enterprise-разработчика.
Для дальнейшего изучения
-
Сторонние контейнеры — Autofac, DryIoc и их преимущества перед встроенным
-
Паттерн Decorator — для кеширования, логирования и cross-cutting concerns
-
Source Generators — автоматическая регистрация сервисов
-
Integration with Hosted Services — правильное внедрение в фоновые задачи
-
Dynamic factories — создание объектов с runtime-параметрами
-
Validation scopes — диагностика проблем с временем жизни
-
Performance optimization — сравнение различных подходов к регистрации

