.NET 依赖注入(DI)全面解析

发布于:2025-06-26 ⋅ 阅读:(19) ⋅ 点赞:(0)

在这里插入图片描述


在这里插入图片描述

一、依赖注入核心原理

1. 控制反转(IoC)与DI关系

  • 控制反转(IoC):框架控制程序流程,而非开发者
  • 依赖注入(DI):IoC的一种实现方式,通过外部提供依赖对象

2. .NET DI核心组件

  • IServiceCollection:服务注册容器
  • IServiceProvider:服务解析器
  • ServiceDescriptor:服务描述符(包含生命周期信息)

二、服务生命周期

1. 三种生命周期类型

生命周期 描述 适用场景
Transient 每次请求创建新实例 轻量级、无状态服务
Scoped 同一作用域内共享实例 Web请求上下文
Singleton 全局单例 配置服务、缓存
// 注册示例
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddSingleton<ISingletonService, SingletonService>();

三、DI容器实现原理

1. 服务注册流程

public static IServiceCollection AddTransient<TService, TImplementation>(this IServiceCollection services)
{
    // 创建服务描述符
    var descriptor = new ServiceDescriptor(
        typeof(TService),
        typeof(TImplementation),
        ServiceLifetime.Transient);
    
    // 添加到集合
    services.Add(descriptor);
    return services;
}

2. 服务解析流程

public object GetService(Type serviceType)
{
    // 1. 查找服务描述符
    var descriptor = _descriptors.FirstOrDefault(d => d.ServiceType == serviceType);
    
    // 2. 根据生命周期创建实例
    if (descriptor.Lifetime == ServiceLifetime.Singleton)
    {
        if (_singletons.TryGetValue(serviceType, out var instance))
            return instance;
        
        instance = CreateInstance(descriptor);
        _singletons[serviceType] = instance;
        return instance;
    }
    // ...处理Scoped和Transient
}

四、高级实现方法

1. 工厂模式注册

services.AddTransient<IService>(provider => {
    var otherService = provider.GetRequiredService<IOtherService>();
    return new ServiceImpl(otherService, "参数");
});

2. 泛型服务注册

services.AddTransient(typeof(IRepository<>), typeof(Repository<>));

3. 多实现解决方案

// 注册多个实现
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<IMessageService, SmsService>();

// 解析时获取所有实现
var services = provider.GetServices<IMessageService>();

五、ASP.NET Core中的DI集成

1. 控制器注入

public class HomeController : Controller
{
    private readonly ILogger _logger;
    
    public HomeController(ILogger<HomeController> logger)
    {
        _logger = logger; // 自动注入
    }
}

2. 视图注入

@inject IConfiguration Config
<p>当前环境: @Config["Environment"]</p>

3. 中间件注入

public class CustomMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    
    public CustomMiddleware(
        RequestDelegate next,
        ILogger<CustomMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }
    
    public async Task InvokeAsync(HttpContext context)
    {
        // 使用注入的服务
        _logger.LogInformation("中间件执行");
        await _next(context);
    }
}

六、自定义DI容器实现

1. 简易DI容器实现

public class SimpleContainer : IServiceProvider
{
    private readonly Dictionary<Type, ServiceDescriptor> _descriptors;
    
    public SimpleContainer(IEnumerable<ServiceDescriptor> descriptors)
    {
        _descriptors = descriptors.ToDictionary(x => x.ServiceType);
    }
    
    public object GetService(Type serviceType)
    {
        if (!_descriptors.TryGetValue(serviceType, out var descriptor))
            return null;
            
        if (descriptor.ImplementationInstance != null)
            return descriptor.ImplementationInstance;
            
        var type = descriptor.ImplementationType ?? descriptor.ServiceType;
        return ActivatorUtilities.CreateInstance(this, type);
    }
}

2. 属性注入实现

public static class PropertyInjectionExtensions
{
    public static void AddPropertyInjection(this IServiceCollection services)
    {
        services.AddTransient<IStartupFilter, PropertyInjectionStartupFilter>();
    }
}

public class PropertyInjectionStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            builder.Use(async (context, nextMiddleware) =>
            {
                var endpoint = context.GetEndpoint();
                if (endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>() is { } descriptor)
                {
                    var controller = context.RequestServices.GetRequiredService(descriptor.ControllerTypeInfo);
                    // 反射实现属性注入
                    InjectProperties(controller, context.RequestServices);
                }
                await nextMiddleware();
            });
            next(builder);
        };
    }
    
    private void InjectProperties(object target, IServiceProvider services)
    {
        var properties = target.GetType().GetProperties()
            .Where(p => p.CanWrite && p.GetCustomAttribute<InjectAttribute>() != null);
            
        foreach (var prop in properties)
        {
            var service = services.GetService(prop.PropertyType);
            if (service != null)
                prop.SetValue(target, service);
        }
    }
}

七、最佳实践

1. 服务设计原则

  • 遵循显式依赖原则
  • 避免服务定位器模式(反模式)
  • 保持服务轻量级

2. 常见陷阱

// 错误示例:捕获Scoped服务到Singleton中
services.AddSingleton<IBackgroundService>(provider => {
    var scopedService = provider.GetRequiredService<IScopedService>(); // 危险!
    return new BackgroundService(scopedService);
});

// 正确做法:使用IServiceScopeFactory
services.AddSingleton<IBackgroundService>(provider => {
    var scopeFactory = provider.GetRequiredService<IServiceScopeFactory>();
    return new BackgroundService(scopeFactory);
});

八、性能优化

1. 避免过度注入

// 不好:注入过多服务
public class OrderService(
    ILogger logger,
    IEmailService emailService,
    ISmsService smsService,
    IRepository repo,
    ICache cache,
    IConfig config)
{
    // ...
}

// 改进:使用聚合服务
public class OrderService(
    ILogger logger,
    INotificationService notification,
    IOrderInfrastructure infra)
{
    // ...
}

2. 编译时注入

[RegisterTransient(typeof(IMyService))]
public class MyService : IMyService
{
    // ...
}

// 使用Source Generator自动生成注册代码
static partial class ServiceRegistration
{
    static partial void AddGeneratedServices(IServiceCollection services)
    {
        services.AddTransient<IMyService, MyService>();
    }
}

.NET的依赖注入系统是框架的核心基础设施,理解其原理和实现方式有助于编写更可测试、更松耦合的应用程序。

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到