实用技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > ASP.NET > 实用技巧 > .NET依赖注入DI

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

作者:百锦再@新空间

依赖注入是一种软件设计模式,其核心思想是将对象依赖关系的管理交由外部容器负责,而不是由对象自身管理,下面小编就来和大家详细介绍一下依赖注入吧

一、依赖注入核心原理

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

2. .NET DI核心组件

二、服务生命周期

三种生命周期类型

生命周期描述适用场景
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的依赖注入系统是框架的核心基础设施,理解其原理和实现方式有助于编写更可测试、更松耦合的应用程序。

到此这篇关于全面解析.NET中的依赖注入(DI)的文章就介绍到这了,更多相关.NET依赖注入DI内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文