依赖注入(DI)是现代 .NET 应用架构的核心模式。ASP.NET Core 内置了完整的 DI 容器,了解其工作原理、生命周期管理、高级注册方式和常见陷阱,对于构建可测试、可维护的企业级应用至关重要。本文将深入探讨 DI 容器的内部机制和最佳实践。
DI 基础回顾 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class OrderService { private readonly SqlOrderRepository _repo = new (); private readonly EmailService _email = new (); } public class OrderService { private readonly IOrderRepository _repo; private readonly INotificationService _notification; public OrderService ( IOrderRepository repo, INotificationService notification ) { _repo = repo; _notification = notification; } }
生命周期管理 1 2 3 4 services.AddSingleton<IAppCache, MemoryCache>(); services.AddScoped<IUnitOfWork, UnitOfWork>(); services.AddTransient<IEmailService, EmailService>();
生命周期选择指南
生命周期
适用场景
线程安全要求
Singleton
配置、缓存、日志、无状态服务
✅ 必须线程安全
Scoped
EF Core DbContext、Unit of Work
❌ 不同请求独立
Transient
轻量级无状态服务、工具类
视情况
生命周期陷阱 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 services.AddSingleton<IService, Service>(); services.AddScoped<IRepository, Repository>(); public class SingletonService : IService { private readonly IServiceScopeFactory _scopeFactory; public SingletonService (IServiceScopeFactory scopeFactory ) { _scopeFactory = scopeFactory; } public async Task ProcessAsync () { using var scope = _scopeFactory.CreateScope(); var repo = scope.ServiceProvider.GetRequiredService<IRepository>(); await repo.SaveAsync(); } }
高级注册技巧 条件注册 1 2 3 4 5 6 7 8 9 10 11 if (builder.Environment.IsDevelopment()) services.AddScoped<IEmailService, FakeEmailService>(); else services.AddScoped<IEmailService, SmtpEmailService>(); if (configuration.GetValue<bool >("Features:UseRedisCache" )) services.AddSingleton<ICacheService, RedisCacheService>(); else services.AddSingleton<ICacheService, MemoryCacheService>();
工厂注册 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 services.AddScoped<IConnection>(sp => { var config = sp.GetRequiredService<IConfiguration>(); var connectionString = config.GetConnectionString("Default" ); return new SqlConnection(connectionString); }); services.AddKeyedScoped<ICacheService, RedisCacheService>("redis" ); services.AddKeyedScoped<ICacheService, MemoryCacheService>("memory" ); public class CacheConsumer { public CacheConsumer ( [FromKeyedServices("redis" )] ICacheService redisCache) { } }
装饰器模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 services.AddScoped<IUserService, UserService>(); services.Decorate<IUserService, LoggingUserService>(); services.Decorate<IUserService, CachingUserService>(); public class LoggingUserService : IUserService { private readonly IUserService _inner; private readonly ILogger _logger; public LoggingUserService (IUserService inner, ILogger<LoggingUserService> logger ) { _inner = inner; _logger = logger; } public async Task<User> GetUserAsync (int id ) { _logger.LogInformation("获取用户 {Id}" , id); return await _inner.GetUserAsync(id); } }
批量注册 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var handlerType = typeof (IHandler<>);services.Scan(scan => scan .FromAssembliesOf<IHandler>() .AddClasses(classes => classes.AssignableTo(handlerType)) .AsImplementedInterfaces() .WithScopedLifetime()); var handlerTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IHandler<>))); foreach (var type in handlerTypes){ foreach (var iface in type.GetInterfaces() .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof (IHandler<>))) { services.AddScoped(iface, type); } }
使用第三方容器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { containerBuilder.RegisterModule<MyAutofacModule>(); }); public class MyAutofacModule : Module { protected override void Load (ContainerBuilder builder ) { builder.RegisterType<UserService>() .As<IUserService>() .InstancePerLifetimeScope(); builder.RegisterType<OrderService>() .PropertiesAutowired(); builder.RegisterType<LoggingInterceptor>(); builder.RegisterType<CachedService>() .InterceptedBy(typeof (LoggingInterceptor)); } }
选项模式(Options Pattern) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class SmtpOptions { public const string SectionName = "Smtp" ; public string Host { get ; set ; } = "" ; public int Port { get ; set ; } = 587 ; public string Username { get ; set ; } = "" ; public string Password { get ; set ; } = "" ; } services.Configure<SmtpOptions>(configuration.GetSection(SmtpOptions.SectionName)); public class EmailService { private readonly SmtpOptions _options; private readonly IOptionsSnapshot<SmtpOptions> _optionsSnapshot; public EmailService (IOptions<SmtpOptions> options ) { _options = options.Value; } public EmailService (IOptionsSnapshot<SmtpOptions> snapshot ) { _optionsSnapshot = snapshot; } public EmailService (IOptionsMonitor<SmtpOptions> monitor ) { monitor.OnChange(newOptions => { Console.WriteLine($"配置已更新: {newOptions.Host} " ); }); } }
验证注入配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (builder.Environment.IsDevelopment()){ var sp = services.BuildServiceProvider(); using var scope = sp.CreateScope(); var service = scope.ServiceProvider.GetRequiredService<IUserService>(); } builder.Host.UseDefaultServiceProvider(options => { options.ValidateOnBuild = true ; options.ValidateScopes = true ; });
ASP.NET Core 中的实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddScoped<IOrderRepository, OrderRepository>(); builder.Services.AddScoped<IOrderService, OrderService>(); public class CustomMiddleware { private readonly RequestDelegate _next; public CustomMiddleware (RequestDelegate next ) => _next = next; public async Task InvokeAsync (HttpContext context, IUserService userService ) { await _next(context); } }
DI 常见反模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class BadService { public void DoWork () { var repo = ServiceLocator.Current.GetInstance<IRepository>(); } } public static class StaticConfig { public static IServiceProvider? ServiceProvider { get ; set ; } } public class GodService { public GodService ( IService1 s1, IService2 s2, IService3 s3, // 6 + 个参数 IService4 s4, IService5 s5, IService6 s6 ) { } } public class EvilSingleton { public EvilSingleton (IUserRepository repo ) { } }
总结 :依赖注入是实现控制反转(IoC)的核心工具。正确理解 Singleton/Scoped/Transient 的生命周期差异、掌握条件注册和工厂注册的灵活性、以及避免生命周期冲突和过度注入,是构建可维护 .NET 应用的基础。ASP.NET Core 内置 DI 容器已足够强大,只有在需要 AOP、属性注入等高级特性时才考虑第三方容器。