依赖注入 DI综合案例

news/2025/3/10 23:09:15/文章来源:https://www.cnblogs.com/suehoo/p/18763965

依赖注入 DI综合案例

需求说明image-20250308115514533

image-20250309135746757

先建一个ConsoleMailSend总项目,然后建一个类库项目

image-20250309144413311

image-20250309144457445

image-20250309144519556

声明一个接口:

image-20250309144632516

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace LogServices
{public interface IlogProvider{public void LogError(string msg);public void LogInfo(string msg);}
}

实现类:

image-20250309144733574

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace LogServices
{public class ConsoleLogProvider:IlogProvider{public void LogInfo(string msg){Console.WriteLine($"ERROR:{msg}");     }public void LogError(string msg){Console.WriteLine($"Info:{msg}");}}
}

然后在新建邮件的一个类库:
image-20250309144920518

image-20250309144944825

声明一个接口:

image-20250309145042380

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace MailService
{public interface IMailService{public void Send(string title, string to, string body);}
}

实现类:

image-20250309145204515

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfigService;
using LogServices;namespace MailService
{public class MailServices : IMailService{private readonly IlogProvider log;private readonly IConfigServices config;public MailServices(IlogProvider log, IConfigServices config){this.log = log;this.config = config;}public void Send(string title, string to, string body){log.LogInfo("准备发邮件");string smtpServer = this.config.GetValue("SmtpServer");string userName = this.config.GetValue("UserName");string password = this.config.GetValue("password");Console.WriteLine($"真发邮件啦!{title},{to}");log.LogInfo("邮件准备发送完毕");}}
}

新建配置服务类库+接口+实现类:

image-20250309145318694

namespace ConfigService
{public interface IConfigServices{public string GetValue(string name);}
}

从环境变量读取:

image-20250309145516899

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConfigService
{public class EnvVarConfigService : IConfigServices{public string GetValue(string name){return Environment.GetEnvironmentVariable(name);}}
}

最后MailSercices添加配置类库的引用:

image-20250309145603323

控制台项目,添加邮件服务的引用:

image-20250309145640747

接着:

image-20250309145809532

image-20250309145917828

最后写控制台:

安装包:

image-20250309150003663

image-20250309150103205

image-20250309150527448

using ConfigService;
using LogServices;
using MailService;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleMailSend
{internal class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<IConfigServices, EnvVarConfigService>();services.AddScoped<IlogProvider, ConsoleLogProvider>();services.AddScoped<IMailService, MailServices>();using (var sp = services.BuildServiceProvider()){var mailService  = sp.GetService<IMailService>();mailService.Send("Hello ", "trump@usa.gov", "懂王你好");}Console.Read();}}
}

image-20250309150544217


新建文件配置读取实现类:

image-20250309150832086

改从文件mail.ini读取配置

SmtpServer=abc.mail.com
UserName=admin
Password = hahaha

image-20250309151406446

image-20250309151759828

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConfigService
{public class IniFileConfigService : IConfigServices{public string FilePath { get; set; }public string GetValue(string name){var kv = File.ReadAllLines(FilePath).Select(s => s.Split("=")).Select(strs => new { Name = strs[0], Value = strs[1] }).SingleOrDefault(kv => kv.Name == name);if (kv != null)return kv.Value;elsereturn null;}}
}

deepseek:

1. 读取文件所有行
csharp
复制
File.ReadAllLines(FilePath)
行为:读取指定路径的文件,返回一个字符串数组,每个元素对应文件中的一行。示例输入:若文件内容为:复制
Server=localhost
Port=8080
2. 按等号分割每一行
csharp
复制
.Select(s => s.Split("="))
行为:将每一行按等号 = 拆分为子字符串数组。示例转换:行 "Server=localhost" → 数组 ["Server", "localhost"]行 "Port=8080" → 数组 ["Port", "8080"]3. 转换为键值对象
csharp
复制
.Select(strs => new { Name = strs[0], Value = strs[1] })
行为:将分割后的数组转换为匿名对象,包含 Name(键)和 Value(值)。示例转换:数组 ["Server", "localhost"] → 对象 { Name = "Server", Value = "localhost" }数组 ["Port", "8080"] → 对象 { Name = "Port", Value = "8080" }4. 按名称查找匹配项
csharp
复制
.SingleOrDefault(kv => kv.Name == name)
行为:查找 Name 属性等于参数 name 的第一个对象。找到 → 返回该对象。未找到 → 返回 null。风险:若存在多个同名键,会抛出 InvalidOperationException(因 SingleOrDefault 要求最多一个匹配项)。

image-20250309152004450

using ConfigService;
using LogServices;
using MailService;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleMailSend
{internal class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();//services.AddScoped<IConfigServices, EnvVarConfigService>();services.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = "mail.ini" });services.AddScoped<IlogProvider, ConsoleLogProvider>();services.AddScoped<IMailService, MailServices>();using (var sp = services.BuildServiceProvider()){var mailService  = sp.GetService<IMailService>();mailService.Send("Hello ", "trump@usa.gov", "懂王你好");}Console.Read();}}
}

deepseek:

在 ASP.NET Core 的依赖注入容器中,AddScoped 方法的参数设计决定了这种写法不可行。以下是详细解释:1. AddScoped 方法的重载分析
ASP.NET Core 的 IServiceCollection 中 AddScoped 有以下关键重载:csharp
复制
// 重载 1:注册实现类型
services.AddScoped(typeof(IConfigServices), typeof(IniFileConfigService));// 重载 2:注册工厂方法
services.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = "mail.ini" 
});
没有直接接受实例的重载(如 AddScoped(Type, object))。若强行传递实例,编译器会报错。2. 为何不能直接传递实例?
依赖注入容器的核心职责是 管理对象的生命周期。直接传递实例(如 new IniFileConfigService)会破坏这一机制:生命周期失控:如果直接传递实例,容器无法控制对象的创建和释放,相当于强制将对象设为单例(Singleton),无论注册为 Scoped 还是 Transient。依赖隔离失效:Scoped 生命周期要求每个作用域内有独立实例,但直接传递实例会共享同一对象,导致状态污染。3. 正确场景对比
场景 1:注册工厂方法(正确)
csharp
复制
services.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = "mail.ini" 
});
行为:每次解析 IConfigServices 时,容器会调用工厂方法创建一个新实例。生命周期:符合 Scoped 语义(同一作用域内复用实例,不同作用域隔离)。场景 2:直接传递实例(错误)
csharp
复制
// 编译错误:不存在匹配的重载方法
services.AddScoped(typeof(IConfigServices), new IniFileConfigService { FilePath = "mail.ini" 
});
问题:容器无法管理预创建实例的生命周期,违背 Scoped 设计原则。4. 特殊例外:单例注册
如果希望强制将实例设为单例,可以使用 AddSingleton 的一个重载:csharp
复制
// 允许直接传递实例(单例)
services.AddSingleton<IConfigServices>(new IniFileConfigService { FilePath = "mail.ini" 
});
原理:单例的生命周期与应用程序一致,容器只需保存引用,无需控制创建过程。5. 为什么 AddSingleton 允许直接传递实例?
单例的特殊性:单例对象在应用程序启动时创建,且全局唯一,无需依赖容器管理其生命周期。Scoped/Transient 的局限性:这两种生命周期需要容器动态控制实例的创建和释放,因此必须通过类型或工厂方法注册。6. 终极解决方案
若希望保持 IniFileConfigService 的配置灵活性,可通过以下方式优化:方案 1:工厂方法注册(推荐)
csharp
复制
services.AddScoped<IConfigServices>(s => new IniFileConfigService { FilePath = "mail.ini" 
});
方案 2:强类型配置绑定
csharp
复制
// 在 appsettings.json 中配置
{"IniConfig": {"FilePath": "mail.ini"}
}// 注册配置类
services.Configure<IniConfig>(Configuration.GetSection("IniConfig"));// 修改服务实现
public class IniFileConfigService : IConfigServices
{private readonly IniConfig _config;public IniFileConfigService(IOptions<IniConfig> config){_config = config.Value;}// ...
}
总结
核心问题:AddScoped 没有直接接受实例的重载,因其生命周期管理需要动态控制对象创建。正确实践:通过工厂方法或配置绑定实现灵活注册。单例例外:仅 AddSingleton 允许直接传递实例,因其生命周期与容器一致

给日志服务添加扩展方法:

image-20250309152518882

image-20250309153237575

using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LogServices;
using Microsoft.Extensions.DependencyInjection;namespace Microsoft.Extensions.DependencyInjection;public static class ConsolelogExtensions
{public static void AddConsoleLog(this IServiceCollection service){service.AddScoped<IlogProvider, ConsoleLogProvider>();   }}

这时候也可以去掉public了:image-20250309153141264

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace LogServices
{class ConsoleLogProvider:IlogProvider{public void LogInfo(string msg){Console.WriteLine($"ERROR:{msg}");     }public void LogError(string msg){Console.WriteLine($"Info:{msg}");}}
}

image-20250309153417985

using ConfigService;
using LogServices;
using MailService;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleMailSend
{internal class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();//services.AddScoped<IConfigServices, EnvVarConfigService>();services.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = "mail.ini" });//services.AddScoped<IlogProvider, ConsoleLogProvider>();services.AddConsoleLog();services.AddScoped<IMailService, MailServices>();using (var sp = services.BuildServiceProvider()){var mailService  = sp.GetService<IMailService>();mailService.Send("Hello ", "trump@usa.gov", "懂王你好");}Console.Read();}}
}

给文件配置添加扩展方法:

image-20250309153712240

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfigService;
using Microsoft.Extensions.DependencyInjection;namespace Microsoft.Extensions.DependencyInjection;public static class IniFileConfigExtensions
{   public static void AddIniFileConfig(this IServiceCollection service,string filepath){service.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = filepath });}}

image-20250309153739652

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConfigService
{public class IniFileConfigService : IConfigServices{public string FilePath { get; set; }public string GetValue(string name){var kv = File.ReadAllLines(FilePath).Select(s => s.Split("=")).Select(strs => new { Name = strs[0], Value = strs[1] }).SingleOrDefault(kv => kv.Name == name);if (kv != null)return kv.Value;elsereturn null;}}
}

image-20250309153842607

image-20250309155719957

其中有一台服务器临时需要改,其他保持不变,引用可覆盖的配置。

新建接口:

image-20250309160700331

image-20250309160741134

实现类:
image-20250309161044206

image-20250309161501436

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace ConfigService
{public class LayeredConfigReader : IConfigReader{private readonly IEnumerable<IConfigServices> services;public LayeredConfigReader(IEnumerable<IConfigServices> services){this.services = services;}public string GetValue(string name){string value = null;foreach (var service in services) { string newVlaue = service.GetValue(name);if (newVlaue != null) { value = newVlaue;}}return value;}}
}

为了方便别人使用,添加扩展方法:
image-20250309161752365

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfigService;
using Microsoft.Extensions.DependencyInjection;namespace Microsoft.Extensions.DependencyInjection;
public static class LayeredConfigEXtensions
{public static void AddLayeredConfig(this IServiceCollection service){service.AddScoped<IConfigReader, LayeredConfigReader>();}}

image-20250309162500916

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConfigService;
using LogServices;namespace MailService
{public class MailServices : IMailService{private readonly IlogProvider log;//private readonly IConfigServices config;private readonly IConfigReader config;public MailServices(IlogProvider log, IConfigReader config){this.log = log;this.config = config;}public void Send(string title, string to, string body){log.LogInfo("准备发邮件");string smtpServer = this.config.GetValue("SmtpServer");string userName = this.config.GetValue("UserName");string Password = this.config.GetValue("Password");Console.WriteLine(smtpServer);Console.WriteLine(userName);Console.WriteLine(Password);Console.WriteLine($"真发邮件啦!{title},{to}");log.LogInfo("邮件准备发送完毕");}}
}

image-20250309164248112

报错:image-20250309164415656

image-20250309164452925

using ConfigService;
using LogServices;
using MailService;
using Microsoft.Extensions.DependencyInjection;
namespace ConsoleMailSend
{internal class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<IConfigServices, EnvVarConfigService>();//services.AddScoped(typeof(IConfigServices), s => new IniFileConfigService { FilePath = "mail.ini" });services.AddIniFileConfig("mail.ini");//services.AddScoped<IlogProvider, ConsoleLogProvider>();services.AddConsoleLog();services.AddLayeredConfig();services.AddScoped<IMailService, MailServices>();using (var sp = services.BuildServiceProvider()){var mailService  = sp.GetService<IMailService>();mailService.Send("Hello ", "trump@usa.gov", "懂王你好");}Console.Read();}}
}

image-20250309164509271

image-20250309164718004

image-20250309164759643

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

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

相关文章

《深入理解计算机网络》 | PDF免费下载 | free download

《深入理解计算机网络》是计算机网络领域的扛鼎之作,由有20余年从业经验的优秀网络技术工程师兼全国网管技能水平开始认证专家王达老师撰写,51CTO技术社区鼎力推荐,权威性毋庸置疑。内容方面,本书结合最新计算机网络技术,全面、系统、深入地阐述了计算机网络的体系结构、工…

《深入理解LINUX内核(第三版)》 | PDF免费下载 | epub free download

《深入理解Linux内核》第3版 将使你了解Linux的所有内部工作,它不仅仅是一个理论上的练习。你将学习到哪些情况下Linux性能最佳,并且你将看到,在大量的不同环境里进行进程调度、文件存取和内存管理时它如何满足提供良好的系统响应的需要。这本书将帮助你充分利用Linux系统。…

Android Studio的配置学习以及整日的总结

所花时间:5h 代码量(行):130 博客量:6 了解的知识点: 首先,今天对于Android Studio的整体配置有了一个新的认识,原因是AS的SDK和虚拟机AVD的默认安装位置在C盘 还有环境变量gradle的下载配置,这些都要了解明白是干什么的,下面我具体的说一下:安装好AS,下载好gradle…

额外添加 _网卡的配置,网络的基础的概念

配置好网卡,上网用 修改网络模式,修改静态ip,动态ip获取方式 system control 系统控制,systemctl 查看当前的上网信息 1.确保你的机器,是连接的网络的,是插上了网线的。(模拟了物理服务器的软件是什么?看你的虚拟的机器(vmware))3.编辑网卡的配置文件 编辑网卡配置文…

用集合说明可以用与或非来表示异或

用集合说明可以用与或非来表示异或 异或(XOR,记作 A⊕BA⊕B)可以通过与(AND,记作 ∧∧)、或(OR,记作 ∨∨)、非(NOT,记作 )的组合来表示。以下是两种常见的表达式形式: 1. 直接组合形式 异或的逻辑可以描述为:当且仅当 A 和 B不同时为真时输出真。 即:A B A⊕B0…

2025.3.10

1,访问PHP文件路径不能出现中文,会显示0行错误 2,<br>换行,<hr>分割线 3,“ ”解析并输出,‘ ’直接输出 4,字符串拼接用. 5,输出 echo "" print_r() 专门输出数组的格式 varr_dump() 数据类型和值6,二维数组

4.9.1 分布偏移的类型

下面介绍一下坐标系中协变量偏移的情况如上图,绿色的曲线是正确的曲线。我们训练的数据是左图,测试的数据是右图。按照左图的数据学习是学不出来绿色曲线的,就会导致右图的数据的准确率很低,这就是协变量偏移

贴现率8%和12%分别计算每个项目的净现值

使用贴现率8%和12%分别计算每个项目的净现值(NPV) JAVA实现 净现值求解实现代码(JAVA):package com.zuoye.Three;import java.math.BigDecimal;public class TieXianLu { public static void main(String[] args) throws Exception { //数据集合 int[] ma…

Netty基础—1.网络编程基础一

大纲 1.什么是OSI开放系统互连 2.OSI七层模型各层的作用 3.TCP/IP协议的简介 4.TCP和UDP的简介 5.TCP连接的三次握手 6.TCP连接的四次挥手 7.TCP/IP中的数据包 8.TCP通过确认应答与序列号提高可靠性 9.HTTP请求的传输过程 10.HTTP协议报文结构 11.Socket、短连接、长连接、网络…

硅基流动+Chatbox实现deepseek R1使用自由

解锁 DeepSeek R1 全能力:高性价比AI对话全流程指南🌟 用最低成本体验顶尖开源模型,对话成本低至 2 分钱/次!一、注册 SiliconFlow 领取千万Tokens▶️ 操作步骤:点击专属链接注册:https://cloud.siliconflow.cn/i/KoKtjLvD 手机验证码登录 → 立即获得 2000万 Tokens(…

3.1.1 线性回归的基本元素

看看批量梯度下降和小批量梯度下降的图形,与我们的理解是相符的注意到小批量梯度下降不是严格单减的,只是趋势是单调减少的(图中的纵轴Cost指的是对于整个训练数据的损失) 每次的批量的大小显然是一个超参数。当批量大小为\(1\)的时候叫做随机梯度下降,当批量大小为\(m\)的…