当前位置: 首页 > news >正文

02020212 .NET Core重难点知识12-服务定位器、.NET依赖注入示例

02020212 .NET Core重难点知识12-服务定位器、.NET依赖注入示例

1. 服务定位器(视频Part2-27)

1.1 IServiceProvider的服务定位器方法
T GetService<T>() → 如果获取不到对象,则返回null。
object GetService(Type serviceType)
T GetRequiredService<T>() → 如果获取不到对象,则抛异常。
object GetRequiredService(Type serviceType)
IEnumerable<T> GetServices<T>() → 适用于可能有很多满足条件的服务。
IEnumerable<object> GetService(Type serviceType)
1.2 其它注册方法
  • 服务类型和实现类型不一致的注册。
  • 简单看看其它Add方法。
1.3 GetService()注册方法
using Microsoft.Extensions.DependencyInjection;
using System;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService, IDisposable{public string Name { get; set; }public void Dispose(){Console.WriteLine("Dispose...");}public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<ITestService, TestServiceImp1>(); // @1 // services.AddScoped<typeof(ITestService), typeof(TestServiceImp1)>(); // @2// services.AddSingleton<ITestService, TestServiceImp1>(); // @3// services.AddSingleton<typeof(ITestService), new TestServiceImp1()>; // @4 TestServiceImp1构造函数可以添加参数using (ServiceProvider sp = services.BuildServiceProvider()){ITestService ts1 = sp.GetService<ITestService>(); // @5 返回的是实现类的对象// TestServiceImp1 ts1 = sp.GetService<TestServiceImp1>(); // @6 返回的是实现类的对象ts1.Name = "Qinway";ts1.SayHi();Console.WriteLine(ts1);}Console.ReadLine();}}
}控制台输出:
Hi! I'm Qinway
Demo02.TestServiceImp1
Dispose...说明:
1. 在@1处两个泛型:ITestService表示服务类型(接口),实现类型TestServiceImp1(实现类)。此时不管实现类是什么,我只要接口的服务。
2. 随着项目变得复杂,写注册服务的人(ITestService)和写实现服务的人(TestServiceImp1),可能不是同一个人来做的。
3. 在@1处和@2处两种写法等价。
4. 在@4处,直接将实现类new好了,这样适用于对象需要传递某个参数的时候使用。可以在构造函数添加参数,由开发者自己控制创建对象的条件。
5. 如果采用@4处的注册形式,那么不用用@5处的获取形式,而是要采用@6处的获取形式。注册类型是实现类,那么拿的时候也是实现类。
6. 在@5和@6处,如果GetService找不到服务,就返回null。
7. GetService方法还有非泛型的方法重载,一般不用。
1.3 GetRequiredService()注册方法
using Microsoft.Extensions.DependencyInjection;
using System;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService, IDisposable{public string Name { get; set; }public void Dispose(){Console.WriteLine("Dispose...");}public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<ITestService, TestServiceImp1>();using (ServiceProvider sp = services.BuildServiceProvider()){TestServiceImp1 ts1 = sp.GetRequiredService<TestServiceImp1>(); // 抛出异常ts1.Name = "Qinway";ts1.SayHi();Console.WriteLine(ts1);}Console.ReadLine();}}
}异常信息:System.InvalidOperationException:“No service for type 'Demo02.TestServiceImp1' has been registered.”说明:GetRequiredService方法不会返回null,这个方法本身就会抛出异常。对比:GetService方法如果找不到,返回null;GetRequiredService如果找不到,抛出异常。这里和C#基础知识显式类型转换(如果不能转换抛异常)和as转换(如果不能转换返回null)类似。
1.4 GetServices()注册方法
  • 该方法适用于实现一个接口有多个服务的情况,如果实现一个接口有多个服务,那么将服务都返回。
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService, IDisposable{public string Name { get; set; }public void Dispose(){Console.WriteLine("Dispose...");}public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}public class TestServiceImp2 : ITestService{public string Name { get; set; }public void SayHi(){Console.WriteLine($"你好,我是{Name}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<ITestService, TestServiceImp1>(); // 注册第一个services.AddScoped<ITestService, TestServiceImp2>(); // 注册第二个using (ServiceProvider sp = services.BuildServiceProvider()){IEnumerable<ITestService> ts1 = sp.GetServices<ITestService>();foreach (ITestService it in ts1){Console.WriteLine(it);}}Console.ReadLine();}}
}控制台输出:
Demo02.TestServiceImp1
Demo02.TestServiceImp2
Dispose...
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService, IDisposable{public string Name { get; set; }public void Dispose(){Console.WriteLine("Dispose...");}public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}public class TestServiceImp2 : ITestService{public string Name { get; set; }public void SayHi(){Console.WriteLine($"你好,我是{Name}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<ITestService, TestServiceImp1>(); // 注册第一个services.AddScoped<ITestService, TestServiceImp2>(); // 注册第二个using (ServiceProvider sp = services.BuildServiceProvider()){var t1 = sp.GetService<ITestService>(); // 如果注册了多个,用Service方法那么获取最后一个。var t2 = sp.GetRequiredService<ITestService>(); // 如果注册了多个,用GetRequiredService方法那么获取的也是最后一个。Console.WriteLine(t1);Console.WriteLine(t2);}Console.ReadLine();}}
}控制台输出:
Demo02.TestServiceImp2
Demo02.TestServiceImp2

2. .NET依赖注入(视频Part-2-28)

  • 在这节课之前,我们讲的GetService等方法都是服务定位器的用法。从这节课开始,是真正的依赖注入。
2.1 依赖注入魅力渐显
  • 依赖注入是有传染性的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中声明的所有服务类型的参数都会被DI赋值。
    • 但是如果一个对象是程序员手动创建的,那么这个对象就和DI没有关系,它的构造函数声明的服务类型参数就不会被自动赋值。
  • .NET的DI默认是构造函数注入。
    • 例如:编写一个类,连接数据库做插入操作,并且记录日志(模拟的输出),把Dao、日志都放入单独的服务类。
    • connstr见备注。
2.2 依赖注入示例
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;namespace Demo02
{class Controller{private readonly ILog log;private readonly IStorage storage;public Controller(ILog log, IStorage storage){this.log = log;this.storage = storage;}public void Test(){this.log.Log("开始上传");this.storage.Save("ABC", "1.txt");this.log.Log("上传完毕");}}interface ILog{public void Log(string msg);}class LogImpl : ILog{public void Log(string msg){Console.WriteLine($"日志:{msg}");}}interface IConfig{public string GetValue(string name);}class ConfigImpl : IConfig{public string GetValue(string name){return "Hello";}}interface IStorage{public void Save(string content, string name);}class StorageImpl : IStorage{private readonly IConfig config;public StorageImpl(IConfig config){this.config = config;}public void Save(string content, string name){string server = config.GetValue("server");Console.WriteLine($"向服务器{server}的文件名为{name}上传{content}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection();services.AddScoped<Controller>();services.AddScoped<ILog, LogImpl>();services.AddScoped<IStorage, StorageImpl>();services.AddScoped<IConfig, ConfigImpl>();using (var sp = services.BuildServiceProvider()){var c = sp.GetRequiredService<Controller>();c.Test();}Console.ReadLine();}}
}控制台输出:
日志:开始上传
向服务器Hello的文件名为1.txt上传ABC
日志:上传完毕说明:
1. 这样做的好处就是业务代码不用动,只需要配置新的服务即可。
2. DI可以降低模块之间的耦合。

结尾

书籍:ASP.NET Core技术内幕与项目实战

视频:https://www.bilibili.com/video/BV1pK41137He

著:杨中科

ISBN:978-7-115-58657-5

版次:第1版

发行:人民邮电出版社

※敬请购买正版书籍,侵删请联系85863947@qq.com※

※本文章为看书或查阅资料而总结的笔记,仅供参考,如有错误请留言指正,谢谢!※

http://www.hskmm.com/?act=detail&tid=340

相关文章:

  • 三数之和-leetcode
  • apache详细配置
  • 9.8总结
  • 相似了
  • 在 AlmaLinux 9 使用 Podman 部署 Redis 7.4.5 并优化内核参数
  • 抖音批量视频下载工具源码C#源码|自动提取DY视频的软件工具
  • AI 检测:精准攻克米饭盒质检难题,赋能食品生产
  • 2025年9月北京中学集训随笔
  • 最新可用Docker镜像加速站点
  • 第一周作业
  • 基于调度场算法将中缀表达式转换为后缀表达式
  • 来此加密实现SSL证书自动申请+自动部署
  • lc1022-从根到叶的二进制数之和
  • 2025.9.9——1橙
  • SIM /api/function/execute 代码执行漏洞
  • C#/.NET/.NET Core技术前沿周刊 | 第 53 期(2025年9.1-9.7)
  • 3
  • linux下安装pycharm时,中文无法显示的问题
  • 学习
  • Docker,Containerd配置私有Harbor仓库和Notary服务器
  • Ubuntu安装notary
  • Ubuntu安装containerd
  • 你的错误处理一团糟-是时候修复它了-️
  • TRVCOST - Travelling cost 题解
  • 我重新制作动画系统的思路
  • 第一次作业:自我介绍+软工5问
  • 第一篇练习博客
  • 原型设计实用干货!3款热门AI生成原型图软件横向测评
  • Python Flask框架入门_3.通过token认证验证API的访问权限(数据库版本)
  • 港科 Tower A 宿舍凝水之谜