.NET CORE DI 依赖注入DI几个概念服务(service):对象;注册服务;服务容器:负责管理注册的服务;查询服务:创建对象及关联对象;对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例)1. .NET中使用DI(1)1....
.NET CORE DI 依赖注入
DI几个概念
服务(service):对象;
注册服务;
服务容器:负责管理注册的服务;
查询服务:创建对象及关联对象;
对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例)
1. .NET中使用DI(1)
1.测试代码见备注
2.根据类型来获取和注册服务
可以分别指定服务类型(service type)和实现类型(implementation type).这两者可能相同,也可能不同.服务类型可以是类,也可以是接口,建议面向接口编程,更灵活.
3…NET控制反转组件取名为DependencyInjection,但他包含ServiceLocator的功能
2. .NET中使用DI(2)
1.Install-Package
Microsoft.Extensions.DependencyInjection
2.using Microsoft.Extensions.DependencyInjection
3.ServiceCollection用来构造容器对象
IServiceProvider.调用ServiceCollection的BuildServiceProvider()创建的ServiceProvider,可以用来获取BuildServiceProvider()之前ServiceCollection中的对象
问题:源 Microsoft Visual Studio Offline Packages 中不存在具有此 ID 的
解决:
一、工具-->Nuget程序包管理器-->程序包管理器设置
二、 Nuget Package Manager-->程序包源
三、点击右上角的 加号按钮 ,添加一个新的Nuget源选项
四、名称:随便取
源:https://api.nuget.org/v3/index.json
点击右侧的 更新 按钮
如果有原包源勾掉禁用,没有忽视
重新生成解决方案,下载可能需要点时间等会就行
实例代码如下:
class Program
{
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddTransient<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
TestServiceImpl t = sp.GetService<TestServiceImpl>();
t.Name = "lily";
t.SayHi();
}
/*TestServiceImpl t = new TestServiceImpl();
t.Name = "tom";
t.SayHi();
Console.Read();*/
}
public interface ITestService
{
public string Name { get; set; }
public void SayHi();
}
public class TestServiceImpl : ITestService
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"Hi,I'm{Name}");
}
}
public class TestServiceImpl2 : ITestService
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"你好,我是{Name}");
}
}
}
3.生命周期
1.给类构造函数中打印,看看不同生命周期的对象创建,使用ServiceProvider.CreateScope()创建Scpoe
2.如果一个类实现了IDisposable接口,则离开作用域之后容器会自动调用对象的Dispose方法
3.不要在长生命周期的对象中引用比它短的生命周期的对象.在ASP.NET Core中,这样做默认会抛异常.
4.生命周期的选择:如果类无状态,建议为Singleton;如果类有状态,且有Scope控制,建议为Scoped,因为通常这种Scope控制下的代码都是运行在同一个线程中的,没有并发修改的问题;在使用Transient的时候要谨慎
5…NET注册服务的重载方法很多
static void Main1(string[] args)
{
ServiceCollection services = new ServiceCollection();
//services.AddTransient<TestServiceImpl>();
//services.AddSingleton<TestServiceImpl>();
services.AddScoped<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
/*TestServiceImpl t = sp.GetService<TestServiceImpl>();
t.Name = "lily";
t.SayHi();
TestServiceImpl t1 = sp.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(t, t1));
t1.Name = "tom";
t1.SayHi();
t.SayHi();*/
using (IServiceScope scope1 = sp.CreateScope())
{
//在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
TestServiceImpl t = scope1.ServiceProvider.GetService<TestServiceImpl>();
t.Name = "lily";
t.SayHi();
TestServiceImpl t1 = scope1.ServiceProvider.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(t, t1));
t1.Name = "tom";
t1.SayHi();
t.SayHi();
}
using (IServiceScope scope2 = sp.CreateScope())
{
//在scope中获取Scope相关的对象,scope1.ServiceProvider而不是sp
TestServiceImpl t = scope2.ServiceProvider.GetService<TestServiceImpl>();
t.Name = "lily";
t.SayHi();
TestServiceImpl t1 = scope2.ServiceProvider.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(t, t1));
t1.Name = "tom";
t1.SayHi();
t.SayHi();
}
}
/*TestServiceImpl t = new TestServiceImpl();
t.Name = "tom";
t.SayHi();
Console.Read();*/
}
4.IServiceProvider的服务定位器方法:
1.T GetService() 如果获取不到对象,则返回null
- Obeject GetService(Type serviceType)
2.T GetRequiredService() 如果获取不到对象,则抛异常
- Object GetRequiredService(Type serviceType)
3.IEnumerable GetService() 适用可能有很多满足条件的服务
- IEnumerable GetService(Type ServiceType)
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<ITestService, TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
//GetService如果找不到服务,就返回null
//ITestService ts1 = sp.GetService<ITestService>();
//Required:必须的,如果找不到直接抛异常
//显式类型转换和as
ITestService ts1 = sp.GetRequiredService<ITestService>();
ts1.Name = "tom";
ts1.SayHi();
IEnumerable<ITestService> test = sp.GetServices<ITestService>();
Console.WriteLine(ts1.GetType());
foreach (ITestService t in test)
{
Console.WriteLine(t.GetType());
}
}
Console.ReadLine();
}
5.真正开始依赖注入
1.依赖注入是有"传染性"的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中生命的所有服务类型的参数都会被DI赋值;但是如果一个对象是程序员手动创建的,那么这个对象就和DI没有关系,它的构造函数中生命的服务类型参数就不会被自动赋值.
2…NET的DI默认是构造函数注入
3.举例:编写一个类,连接数据库做插入操作,并且记录日志(模拟的输出),把Dao,日志都放入单独的服务类
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())
{
Controller c = sp.GetRequiredService<Controller>();
c.Test();
}
Console.ReadKey();
}
}
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()
{
log.Log("开始上传");
this.storage.Save("xiaomi", "1.txt");
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}");
}
}
6.原理复刻
需求说明
1.有配置服务,日志服务,然后再开发一个邮件发送器服务.可以通过配置服务来从文件,环境变量,数据库等地方读取配置,可以通过日志服务来将程序运行过程中的日志信息写入文件,控制台,数据库等
2.说明:案例中开发了自己的日志,配置等接口,这只是在揭示原理,.NET有现成的
实现1
1.创建三个.NET Core类库项目,ConfigServices是配置服务的项目
LogServices是日志服务的项目
MailServices是邮件发送器的项目
然后再建一个.NET Core控制台项目引用ConfigServices项目和LogServices项目,而MailServicesConsole项目引用MailServices项目.
2.编写类库项目LogServices,创建ILogProvider接口.编写实现类ConsoleLogProvider.编写一个ConsoleLogProviderExtensions定义扩展方法AddConsoleLog,namespace和IServiceCollection一致
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<IConfigService>(s=>new IniFileConfigService {FilePath = "mail.ini" });
//services.AddScoped<IConfigService,EnvVarConfigService>();
services.AddScoped<IMailService, MailServiceImpl>();
//services.AddScoped<ILogProvider, ConsoleLogProvider>();
services.AddConsoleLog();
using (var sp = services.BuildServiceProvider())
{
//第一个对象只能用ServiceLocator
IMailService mailService = sp.GetRequiredService<IMailService>();
mailService.Send("Hello", "trump@usa.gov", "Make great again");
}
Console.Read();
}
本文标题为:.NET CORE DI 依赖注入
基础教程推荐
- C#控制台实现飞行棋小游戏 2023-04-22
- winform把Office转成PDF文件 2023-06-14
- C# List实现行转列的通用方案 2022-11-02
- linux – 如何在Debian Jessie中安装dotnet core sdk 2023-09-26
- ZooKeeper的安装及部署教程 2023-01-22
- unity实现动态排行榜 2023-04-27
- C# windows语音识别与朗读实例 2023-04-27
- C#类和结构详解 2023-05-30
- C# 调用WebService的方法 2023-03-09
- 一个读写csv文件的C#类 2022-11-06