Основы DI
Dependency Injection — это паттерн, при котором объекты получают свои зависимости снаружи, а не создают их самостоятельно.
▸Интерфейс и реализация
1public interface IEmailService2{3 Task SendAsync(string to, string subject, string body);4}56public class SmtpEmailService : IEmailService7{8 private readonly SmtpClient _client;910 public SmtpEmailService(SmtpClient client)11 {12 _client = client;13 }1415 public async Task SendAsync(string to, string subject, string body)16 {17 await _client.SendMailAsync("from@example.com", to, subject, body);18 }19}
Регистрация сервисов
1// Program.cs2builder.Services.AddControllers();34// Scoped — один экземпляр на запрос5builder.Services.AddScoped<IEmailService, SmtpEmailService>();67// Singleton — один экземпляр на всё приложение8builder.Services.AddSingleton<ICacheService, MemoryCacheService>();910// Transient — новый экземпляр каждый раз11builder.Services.AddTransient<ILogger, ConsoleLogger>();
▸Жизненные циклы
| Lifetime | Описание | Использование |
|----------|----------|---------------|
| Singleton | Один экземпляр | Кэш, конфигурация |
| Scoped | Один на запрос | БД, Http-клиент |
| Transient | Каждый вызов | Stateless сервисы |
Инжекция через конструктор
1[ApiController]2[Route("api/[controller]")]3public class UsersController : ControllerBase4{5 private readonly IUserService _userService;6 private readonly IEmailService _emailService;7 private readonly ILogger<UsersController> _logger;89 public UsersController(10 IUserService userService,11 IEmailService emailService,12 ILogger<UsersController> logger)13 {14 _userService = userService;15 _emailService = emailService;16 _logger = logger;17 }1819 [HttpPost]20 public async Task<IActionResult> Create(CreateUserDto dto)21 {22 _logger.LogInformation("Creating user {Email}", dto.Email);2324 var user = await _userService.CreateAsync(dto);25 await _emailService.SendAsync(dto.Email, "Welcome", "...");2627 return CreatedAtAction(nameof(GetById), new { id = user.Id }, user);28 }29}
Фабрики
1// Использование фабрики для создания сервисов2builder.Services.AddScoped<IRepositoryFactory, RepositoryFactory>();34public interface IRepositoryFactory5{6 IRepository<T> Create<T>() where T : class;7}89public class RepositoryFactory : IRepositoryFactory10{11 private readonly IServiceProvider _provider;1213 public RepositoryFactory(IServiceProvider provider)14 {15 _provider = provider;16 }1718 public IRepository<T> Create<T>() where T : class19 {20 return _provider.GetRequiredService<IRepository<T>>();21 }22}
Декораторы
1// Регистрация декоратора2builder.Services.AddScoped<IUserService, UserService>();3builder.Services.AddScoped<IUserService>(sp =>4{5 var inner = sp.GetRequiredService<IUserService>();6 return new LoggingUserService(inner);7});89public class LoggingUserService : IUserService10{11 private readonly IUserService _inner;1213 public LoggingUserService(IUserService inner)14 {15 _inner = inner;16 }1718 public async Task<User> GetByIdAsync(int id)19 {20 Console.WriteLine($"Getting user {id}");21 var result = await _inner.GetByIdAsync(id);22 Console.WriteLine($"Got user {result.Name}");23 return result;24 }25}
Автоматическая регистрация
1// Регистрация всех сервисов из assembly2builder.Services.Scan(scan => scan3 .FromAssemblyOf<Startup>()4 .AddClasses(classes => classes.Where(t => t.Name.EndsWith("Service")))5 .AsImplementedInterfaces()6 .WithScopedLifetime());
Тестирование
1// Мокирование для тестов2var mockService = new Mock<IUserService>();3mockService.Setup(s => s.GetByIdAsync(1))4 .ReturnsAsync(new User { Id = 1, Name = "Test" });56var controller = new UsersController(mockService.Object, ...);
Заключение
Dependency Injection — это основа поддерживаемого и тестируемого кода в ASP.NET Core. Правильный выбор жизненного цикла и использование фабрик позволяют создавать гибкие приложения.