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

在这里插入图片描述

文章目录

    • 一、依赖注入核心原理
      • 1. 控制反转(IoC)与DI关系
      • 2. .NET DI核心组件
    • 二、服务生命周期
      • 1. 三种生命周期类型
    • 三、DI容器实现原理
      • 1. 服务注册流程
      • 2. 服务解析流程
    • 四、高级实现方法
      • 1. 工厂模式注册
      • 2. 泛型服务注册
      • 3. 多实现解决方案
    • 五、ASP.NET Core中的DI集成
      • 1. 控制器注入
      • 2. 视图注入
      • 3. 中间件注入
    • 六、自定义DI容器实现
      • 1. 简易DI容器实现
      • 2. 属性注入实现
    • 七、最佳实践
      • 1. 服务设计原则
      • 2. 常见陷阱
    • 八、性能优化
      • 1. 避免过度注入
      • 2. 编译时注入

在这里插入图片描述

一、依赖注入核心原理

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/91624.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/91624.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8S部署ELK(二):部署Kafka消息队列

目录 1. Kafka 简介 1.1 Kafka 核心概念 &#xff08;1&#xff09;消息系统 vs. 流处理平台 &#xff08;2&#xff09;核心组件 1.2 Kafka 核心特性 &#xff08;1&#xff09;高吞吐 & 低延迟 &#xff08;2&#xff09;持久化存储 &#xff08;3&#xff09;分…

Rust进阶-part1-智能指针概述-box指针

Rust进阶[part1]_智能指针概述&box指针 智能指针概述 在Rust中,智能指针是一类特殊的数据结构,它们不仅像普通指针一样可以引用数据,还带有额外的元数据和功能。与普通指针不同,智能指针通常使用结构体实现,并且会实现 Deref 和 Drop 等特定的trait,以提供更强大的…

C++扩展 --- 并发支持库(补充1)

C扩展 --- 并发支持库&#xff08;下&#xff09;https://blog.csdn.net/Small_entreprene/article/details/149606406?fromshareblogdetail&sharetypeblogdetail&sharerId149606406&sharereferPC&sharesourceSmall_entreprene&sharefromfrom_link atom…

在Three.js中导入和添加自定义网格的最佳实践 - 综合指南

探索在Three.js中导入和添加自定义网格的最佳实践。本指南涵盖增强 3D 项目的技术、技巧和实际示例。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 强烈建议使用 GLTF 格式来集成 3D 几何体&#xff0c;提供简化的流程&#xff0c;并固有地支持动画、…

Redis知识点(1)

目录 Redis Redis和MySQL的区别 Redis的高可用方案 Redis可以用来做什么 Redis的数据类型 字符串 列表 哈希 集合 有序集合 Bitmap Redis为什么快呢&#xff1f; I/O多路复用 说说select,poll,epoll,kqueue,IOCP的区别 Redis为什么早期选择单线程&#xff1f; …

使用iptables封禁恶意ip异常请求

查看后端日志发现一IP&#xff08;103.76.250.29&#xff09;频繁请求不存在的资源路径​​&#xff08;如 /api/v1/guest/comm/config、/theme/default/assets/compoments.js 等&#xff09;&#xff0c;并伴随对根路径 / 的正常访问。这种行为的可能性包括恶意扫描、自动化工…

BehaviorTree.Ros2 编译教程

1. 源码下载 git clone https://github.com/BehaviorTree/BehaviorTree.ROS2.git2. 编译过程 源码中有3个项目: btcpp_ros2_interfacesbtcpp_ros2_interfacesbtcpp_ros2_samples 2.1 编译btcpp_ros2_interfaces: colcon --packages-select btcpp_ros2_interfaces2.2 编译 …

AR智能巡检系统:制造业设备管理的效率革新

随着工业4.0和数字化转型的加速&#xff0c;设备管理在制造业、能源、交通等关键领域的重要性愈发凸显。传统设备巡检依赖人工记录和纸质报告&#xff0c;不仅效率低下&#xff0c;还容易因人为疏忽导致数据错误或安全隐患。然而&#xff0c;增强现实&#xff08;AR www.teamhe…

破解海外仓客户响应难题:自动化系统是关键

在跨境电商蓬勃发展的当下&#xff0c;海外仓作为连接卖家与终端消费者的重要枢纽&#xff0c;其服务效率直接影响着卖家的运营成果。其中&#xff0c;即时客户响应一直是行业痛点&#xff0c;尤其对中小型海外仓而言&#xff0c;单纯依靠人力维持全天候服务意味着高昂的成本压…

PyTorch基础——张量计算

文章目录PyTorch基础——张量计算1 什么是张量计算&#xff1f;2 基本算术运算2.1 加法运算2.1.2 torch.add2.1.3 a.add(b) 与 a.add_(b)a.add(b) 方法a.add_(b) 方法核心区别2.2 减法运算2.2.1 toch.sub()2.2.2 a.sub(b) 和a.sub_(b)a.sub(b) 方法a.sub_(b) 方法核心区别使用建…

云原生联调利器:Telepresence实战

Telepresence在云原生联调中的应用&#xff1a;本地服务直连K8s集群实战在云原生开发中&#xff0c;调试和测试服务常常需要本地环境与远程Kubernetes&#xff08;K8s&#xff09;集群无缝集成。Telepresence是一个开源工具&#xff0c;它允许开发者将本地服务“注入”到K8s集群…

浏览器【详解】requestIdleCallback(浏览器空闲时执行)

简介requestIdleCallback 是浏览器的一个 API&#xff0c;用于在浏览器空闲时间执行低优先级任务&#xff0c;避免阻塞主线程&#xff0c;提升页面性能和响应速度。 当浏览器完成了关键任务&#xff08;如渲染、布局、用户交互处理&#xff09;且暂时没有更高优先级的工作时&am…

STP技术

一、环路的危害1.现象链路指示灯快速闪烁MAC表震荡&#xff1a;交换机频繁修改MAC地址表 → 转发失效。2.环路危害造成的影响链路堵塞主机操作系统响应迟缓二层交换机管理缓慢冲击网关设备的CPU三、STP的作用1.STP基本原理STP即生成树协议&#xff0c;它通过阻断冗余链路来消除…

RAGFLOW~knowledge graph

start 为了增强多跳问答&#xff0c;RAGFlow在数据提取和索引之间增加了一个知识图谱构建步骤&#xff0c;如下面所示。这一步骤会从您指定的分块方法生成的现有块中创建额外的块。 从v0.16.0版本开始&#xff0c;RAGFlow支持在知识库上构建知识图谱&#xff0c;允许你在知识库…

机器学习【二】KNN

KNN算法是一种基于实例的惰性学习算法&#xff0c;其核心思想是通过"多数投票"机制进行分类决策。算法流程包括数据准备&#xff08;需归一化处理&#xff09;、距离计算&#xff08;常用欧氏距离&#xff09;、选择K值&#xff08;通过交叉验证确定&#xff09;和决…

preloader

patch调试串口115200--- a/platform/ac8257/default.makb/platform/ac8257/default.mak-40,7 40,7 CFG_USB_DOWNLOAD :1CFG_FUNCTION_PICACHU_SUPPORT :1CFG_PMT_SUPPORT :0CFG_UART_COMMON :1 -CFG_LOG_BAUDRATE :921600 CFG_LOG_BAUDRATE :115200CFG_EVB_UART_CLOCK :260000…

Linux基础(三)——Bash基础

1、Bash基础1.1 Bash简介从前边操作系统的组成介绍中&#xff0c;我们可以知道操作系统为上层用户提供的与内核进行交互的接口称为shell&#xff0c;其在系统中的位置如下图所示&#xff0c;shell作为内核和用户之间的中介&#xff0c;接收用户发送的指令&#xff0c;将其解析为…

Python 元编程实战:动态属性与数据结构转换技巧

在处理复杂嵌套的 JSON 数据源时&#xff0c;我们常面临访问不便、结构不灵活、字段关联性差等问题。本文将以 O’Reilly 为 OSCON 2014 提供的 JSON 数据源为例&#xff0c;系统讲解如何通过 动态属性转换、对象封装、数据库映射与特性&#xff08;property&#xff09;机制&a…

Android-侧边导航栏的使用

在学习之前&#xff0c;我们先得知道侧边导航栏是什么&#xff1f;它是一个 可以让内容从屏幕边缘滑出的布局容器&#xff0c;由安卓官方提供&#xff0c;用于创建侧边菜单&#xff0c;通常搭配 NavigationView 使用&#xff1b;添加依赖&#xff1a;在app下的build.gradle中添…

lesson30:Python迭代三剑客:可迭代对象、迭代器与生成器深度解析

目录 一、可迭代对象&#xff1a;迭代的起点 可迭代对象的本质特征 可迭代对象的工作原理 自定义可迭代对象 二、迭代器&#xff1a;状态化的迭代工具 迭代器协议与核心方法 迭代器的状态管理 内置迭代器的应用 三、生成器&#xff1a;简洁高效的迭代器 生成器函数&a…