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

02020211 .NET Core重难点知识11-依赖注入、.NET中DI服务注册、服务的生命周期、IDisposable接口示例

02020211 .NET Core重难点知识11-依赖注入、.NET中DI服务注册、服务的生命周期、IDisposable接口示例

1. 依赖注入概念(视频Part2-24)

1.1 依赖注入概述
  • 生活中的控制反转:自己发电和用电网的电。
    • 传统控制 → 什么东西都是自己拼装,自己组装。
    • 反转控制 → 我不需要知道怎么弄,我要这个东西的时候别人就给我了。
  • 依赖注入(Dependency Injection,DI)是控制反转(Inversion of Control,IOC)思想的实现方式。
  • 依赖注入简化模块的组装过程,降低模块之间的耦合度。
1.2 自己发电的代码
var connSetting = ConfigurationManager.ConnectionStrings["connStr1"]; // 从配置文件取出数据
string connStr = connSetting.ConnectionString; // 将数据转换为字符串
SqlConnection conn = new SqlConection(connStr); // 通过字符串连接数据库优点:什么东西都是自己搞,对于细节很清楚,自己可以做一些比较精细化的控制。缺点:要求自己对一切都非常清楚,连接字符串从哪里来,需要调用哪个类,构造函数是怎么样的,需要创建哪些对象。
1.3 控制反转的目的
  • 从怎样创建XX对象 → 转为 → 我要XX对象。
  • 两种实现方式:
    • 服务定位器(ServiceLocator)
    • 依赖注入。
// 实现方式1:服务定位器。如下是伪代码,理解即可。
IDbConnection conn = ServiceLocation.GetService<IDbConnection>();说明:
1. ServiceLocation类是服务定位器,它有一个GetService方法,IDbConection是连接对象
2. GetService方法需要IDbConection这个数据库连接对象。
—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—·—
// 实现方式2:依赖注入
class Demo
{public IDbConnection Conn {get; set;} // 声明一个IDbConnection类型的属性Conn。public void InsertDB(){IDbCommand cmd = Conn.CreateCommand();}
}说明:
1. 在Demo类中声明了一个Conn属性。
2. 在创建Demo类对象的时候,框架会自动给属性赋值。此时你不管这个值是怎么来的,只要创建了对象,那么框架就自动给属性赋值了。

2. .NET中DI服务注册(视频Part2-25)

2.1 DI几个概念
  • 服务(Service)→ 对象。服务就是你管框架要的对象。
    • 我要一个数据库操作对象。
    • 我要一个文件操作对象。
  • 注册服务 → 对象不是凭空而来的,你要一个对象,那么这个对象怎么来的呢?
    • 在要对象之前,你需要先把这个对象注册进去,然后才能拿到这个服务。
  • 服务容器 → 负责管理注册的服务。注册服务,要服务都通过服务容器来实现。
    • 注册服务到服务容器,从服务容器中要服务。
  • 查询服务 → 创建对象及关联对象。
  • 对象声明周期 → 从服务容器中那的服务,本身就是一个对象。这个对象你拿过来了,什么时候销毁,这就是指这个对象的生命周期。
    • Transient(瞬态):对象拿过来用完了就马上销毁。
    • Scoped(范围):拿过来用也会被我这个范围的其它人用,等这个范围用完之后再销毁。
    • Singleton(单例):无论谁获取服务,拿到的都是同一个对象。
2.2 .NET中的DI
  • 根据类型来获取和注册服务。可以分别指定服务类型(Service Type)和实现类型(Implementation Type)。这两者可能相同,也可能不同。服务类型可以是类,也可以是接口。建议面向接口编程,更灵活。
  • .NET控制反转组件取名为DependencyInjection(即依赖注入),但它包含ServiceLocation(即服务定位器)的功能。
using System;
using System.Linq;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService{public string Name { get; set; }public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}class Program{static void Main(string[] args){TestServiceImp1 t1 = new TestServiceImp1();t1.Name = "Qinway";t1.SayHi();Console.ReadLine();}}
}控制台输出:
Hi! I'm Qinway说明:如上是常规用法,需要创建接口实现类的对象,然后调用方法。
2.3 .NET中使用DI的步骤
step1 → Install-Package Microsoft.Extensions.DependencyInjection // 安装依赖反转的包
step2 → using Microsoft.Extension.DependencyInjection
step3 → ServiceCollection用来构造容器对象IServiceProvider。// 注册
step4 → 调用ServiceColection的BuildServiceProvider(),用来创建ServiceProvider对象。// 创建
step5 → 通过ServiceProvider对象,可以获取BuildServiceProvider()之前ServiceCollection中的对象。 // 使用
2.4 使用DI示例
  • 首先项目中需要安装NuGet包:Install-Package Microsoft.Extensions.DependencyInjection
图片链接丢失
  • 截止2025.09.07日,该包的版本号为:Microsoft.Extensions.DependencyInjection 9.0.8
  • 老师课上演示改包的版本号为:Microsoft.Extensions.DependencyInjection 5.0.0
// 如下代码以Microsoft.Extensions.DependencyInjection 9.0.8版本演示
using Microsoft.Extensions.DependencyInjection;
using System;namespace Demo02
{public interface ITestService{public string Name { get; set; }public void SayHi();}public class TestServiceImp1 : ITestService{public string Name { get; set; }public void SayHi(){Console.WriteLine($"Hi! I'm {Name}");}}class Program{static void Main(string[] args){ServiceCollection services = new ServiceCollection(); // @1 ServiceCollection是一个集合services.AddTransient<TestServiceImp1>(); // @2 添加瞬态服务using (ServiceProvider sp = services.BuildServiceProvider()) // @3 创建ServiceProvider对象{TestServiceImp1 t1 = sp.GetService<TestServiceImp1>(); // @4 t1需要TestServiceImp1对象t1.Name = "Qinway";t1.SayHi();}Console.ReadLine();}}
}说明:
1. 在@1处,创建一个存放服务的集合services。
2. 在@2处,给services集合添加一个瞬态服务TestServiceImp1
3. 在@3处,ServiceProvider实现了IDisposable接口的,要用using()括起来,不然会存在资源泄露的问题。
3.1 等号右边创建了ServiceProvider对象,这个对象相当于ServiceLocation(服务定位器)。
3.2 等号左边创建了一个TestServiceImp1类型变量t1。
4. 在@4处,t1要一个服务,即TestServiceImp1类型的对象
5. 对比本例代码和2.2中的代码。
5.1 在2.2中TestServiceImp1 t1 = new TestServiceImp1();一行代码就搞定的,在本例中搞了8行代码,闲得蛋疼。
5.2 目前来讲看不出来任何意义,后面会继续讲解。

3. 服务的生命周期(视频Part2-26)

3.1 生命周期
  • 给类构造函数中打印,看看不同声明周期的对象创建。
    • 使用serviceProvider.CreateScope()创建Scope。
  • 如果一个类实现了IDisposable接口,则离开作用域之后容器会自动调用对象的Dispose方法。
  • 不要在长生命周期的对象中引用比它短的生命周期的对象。
    • 在ASP.NET Core中,这样做默认会抛出异常。
  • 生命周期的选择
    • 如果类无状态,建议为Singleton。
      • 无状态的类:一个类没有属性,没有成员变量。
      • 无状态的类没有并发的问题。
      • 无状态的类声明为Singleton,可以减少内存占用。
    • 如果类有状态,且有Scope控制,建议为Scoped。因为通常这种Scope控制下的代码都是运行在同一个线程中的,没有并发问题;在使用Transient的时候要谨慎,因为每次会创建一个新的对象,占用更多的内存。
  • .NET注册服务的重载方法很多,看文档琢磨。
3.2 Transient(瞬态)示例
static void Main(string[] args)
{ServiceCollection services = new ServiceCollection();services.AddTransient<TestServiceImp1>(); // Transient,瞬态using (ServiceProvider sp = services.BuildServiceProvider()){TestServiceImp1 t1 = sp.GetService<TestServiceImp1>();TestServiceImp1 t2 = sp.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t1, t2)); // → False。}
}说明:ReferenceEquals方法用来比较两个变量指向的是否是同一个对象。如果是同一个对象返回True,如果不是同一个对象返回False。注意,如果为瞬态服务,那么每次创建的都是新的对象。
3.3 Singleton(单例)示例
static void Main(string[] args)
{ServiceCollection services = new ServiceCollection();services.AddSingleton<TestServiceImp1>(); // Singleton,单例using (ServiceProvider sp = services.BuildServiceProvider()){TestServiceImp1 t1 = sp.GetService<TestServiceImp1>();TestServiceImp1 t2 = sp.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t1, t2)); // → Truet1.Name = "Qinway";t1.SayHi(); // → Hi! I'm Qinwayt2.Name = "QW"; // 修改t2的属性t1.SayHi(); // → Hi! I'm QW。此时t1指向的对象属性也改变了。}Console.ReadLine();
}注意,如果为单例服务,那么每次都是指向同一个对象。
3.4 Scope(范围)示例
  • Scope对象,是由程序员自己设定的。
static void Main(string[] args)
{ServiceCollection services = new ServiceCollection();services.AddScoped<TestServiceImp1>(); // Scoped,范围using (ServiceProvider sp = services.BuildServiceProvider()){TestServiceImp1 tt; // @1 定义一个变量,用来获取第一个Scope范围的对象。using (IServiceScope scope1 = sp.CreateScope()) // using定义了第一个Scope的范围。{// 在scope中获取Scope相关的对象,是scope1.ServiceProvider而不是sp。TestServiceImp1 t1 = scope1.ServiceProvider.GetService<TestServiceImp1>();TestServiceImp1 t2 = scope1.ServiceProvider.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t1, t2)); // → Truett = t1; // @2}using (IServiceScope scope2 = sp.CreateScope()) // using定义了第二个Scope的范围。{TestServiceImp1 t3 = scope2.ServiceProvider.GetService<TestServiceImp1>();TestServiceImp1 t4 = scope2.ServiceProvider.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t3, t4)); // → TrueConsole.WriteLine(object.ReferenceEquals(tt, t3)); // → Fasle @3}}Console.ReadLine();
}说明:在实际开发中在@1处定义的变量,不要在@2处使用内部范围的变量。这里为了演示,后续开发中要避免。

4. IDisposable接口实现示例

  • 一个类实现了IDisposable接口,则离开作用域之后容器会自动调用对象的Dispose方法,这样方便内存的管理。
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<TestServiceImp1>();using (ServiceProvider sp = services.BuildServiceProvider()){using (IServiceScope scope1 = sp.CreateScope()){TestServiceImp1 t1 = scope1.ServiceProvider.GetService<TestServiceImp1>();TestServiceImp1 t2 = scope1.ServiceProvider.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t1, t2));} // 离开第一个范围,自动调用Dispose方法using (IServiceScope scope2 = sp.CreateScope()){TestServiceImp1 t3 = scope2.ServiceProvider.GetService<TestServiceImp1>();TestServiceImp1 t4 = scope2.ServiceProvider.GetService<TestServiceImp1>();Console.WriteLine(object.ReferenceEquals(t3, t4));} // 离开第二个范围,自动调用Dispose方法}Console.ReadLine();}}
}控制台输出:
True
Dispose...
True
Dispose...

结尾

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

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

著:杨中科

ISBN:978-7-115-58657-5

版次:第1版

发行:人民邮电出版社

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

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

http://www.sczhlp.com/news/77847/

相关文章:

  • 河南网站建设官网百度怎么搜图片
  • 哪些网站推广不收费裕华区建设局网站
  • 免费建立网站的网站都有啥学做网站论坛vip学员码
  • 做期货关注网站深圳外贸招聘
  • 东莞优化网站建设中文html网站模板下载
  • 生道网站建设平台四川营销型网站
  • 对于时间管理的感悟 - Charlie
  • 旅游网站有哪些功能黄山市旅游攻略
  • 收费底的网站有吗做电脑壁纸的网站
  • 如何在百度做网站app开发 深圳
  • 时间轴网站设计网站地图制作怎么做
  • 四合一网站建设源码自己怎样成为电商
  • 网站开发兼容ie个人与公司网站备案
  • 网站建设经营特色漂亮公司网站源码打包下载
  • 网站建设课程有哪些收获网站建设丶金手指专业
  • 有了网站怎样做公众号网站的设计 哪家网络公司好
  • 重庆网站排名优化教程图灵机器人 wordpress
  • 软件工程第一次作业-自我介绍
  • Revit 创建自适应族
  • 表达式树复用陷阱:为什么结果会“颠倒”?
  • 建设事业单位网站多少钱上海建设银行网站上班时间
  • .net 创建网站项目网站建设业务的途径
  • wordpress技巧:开启wordpress多站点功能公司建设网站需要什么资质
  • php 企业网站管理系统电子商务网站建设策划书
  • 网站建设捌金手指花总十九html网页设计题库
  • 郴州做网站公司wordpress登录密码忘
  • 贵州省住房和建设厅网网站首页win2012 iis添加网站
  • 济南网站建设老威wordpress 分类名称
  • 金华住房与城乡建设部网站烟台手机网站建设电话
  • 站酷设计网站官网入口下载网页设计师常逛网站