依赖注入(Dependency Injection,DI)是控制反转(Inversion of Control,IOC)思想的实现方式。
控制反转的两种实现方式:
服务(service):对象;
注册服务;
服务容器:负责管理注册的服务;
查询服务:创建对象及关联对象;
对象生命周期:Transient
(瞬态); Scoped
(范围); Singleton
(单例);
1、Install-Package Microsoft.Extensions.DependencyInjection
2、using Microsoft.Extensions.DependencyInjection
3、ServiceCollection
用来构造容器对象IServiceProvider
。调用ServiceCollection
的BuildServiceProvider()
创建的ServiceProvider
,可以用来获取BuildServiceProvider()
之前ServiceCollection
中的对象。
internal interface ITestService
{
public string Name { get; set; }
public void SayHi();
}
internal class TestServiceImpl
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"Hi, I'm {Name}");
}
}
internal class TestServiceImpl2
{
public string Name { get; set; }
public void SayHi()
{
Console.WriteLine($"你好,我是{Name}");
}
}
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddTransient<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
TestServiceImpl testService = sp.GetRequiredService<TestServiceImpl>();
testService.Name = "tom";
testService.SayHi();
}
}
Transient:
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddTransient<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
var ts1 = sp.GetService<TestServiceImpl>();
var ts2 = sp.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(ts1, ts2));
}
}
输出:False
Singleton:
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddSingleton<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
var ts1 = sp.GetService<TestServiceImpl>();
var ts2 = sp.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(ts1, ts2));
}
}
输出:True
Scoped:
static void Main(string[] args)
{
ServiceCollection services = new ServiceCollection();
services.AddScoped<TestServiceImpl>();
using (ServiceProvider sp = services.BuildServiceProvider())
{
using (IServiceScope scope = sp.CreateScope())
{
var ts1 = scope.ServiceProvider.GetService<TestServiceImpl>();
var ts2 = scope.ServiceProvider.GetService<TestServiceImpl>();
Console.WriteLine(object.ReferenceEquals(ts1, ts2));
}
}
}
输出:True
GetService
和GetRequiredService
有生命区别?
GetService
如果找不到注入的类会返回null,
GetRequiredService
如果找不到注入的类会抛出异常。
Tips: 生命周期的选择:如果类无状态,建议为Singleton
;如果类有状态,且有Scope
控制,建议为Scoped
,因为通常这种Scope
控制下的代码都是运行在同一个线程中的,没有并发修改的问题;在使用Transient
的时候要谨慎。
T GetService<T>()
如果获取不到对象,则返回null。object GetService(Type serviceType)
T GetRequiredService<T>()
如果获取不到对象,则抛异常object GetRequiredService(Type serviceType)
IEnumerable<T> GetServices<T>()
适用于可能有很多满足条件的服务IEnumerable<object> GetServices(Type serviceType)
总结: 1、依赖注入是有“传染性”的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中声明的 所有服务类型的参数都会被DI赋值;但是如果一个对象是程序员手动创建的,那么 这个对象就和DI没有关系,它的构造函数中声明的服务类型参数就不会被自动赋值。 2、.NET的DI默认是 构造函数注入。 3、第三方DI容器:Autofac等。Autofac优点:支持属性注入、基于名字注入、基于约定的注入等。
1、创建一个json文件,文件名随意,比如config.json,设置“ 如果较新则复制”。
2、NuGet安装Microsoft.Extensions.Configuration
和Microsoft.Extensions.Configuration.Json
。
3、
config.json
{
"exclude": [
"**/bin",
"**/bower_components",
"**/jspm_packages",
"**/node_modules",
"**/obj",
"**/platforms"
],
"name": "yh",
"age": 18,
"proxy": {
"address": "bbaa",
"port": 80
}
}
static void Main(string[] args)
{
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile(
"config.json", optional: false, reloadOnChange: false);
IConfigurationRoot config = configBuilder.Build();
string name = config["name"];
string proxyAddress = config.GetSection("proxy:address").Value;
Console.WriteLine(name);
Console.WriteLine(proxyAddress);
}
optional参数:表示这个文件是否可选。初学时,建议optional设置为false,这样写错了的话能够及时发现。 reloadOnChange参数:表示如果文件修改了,是否重新加载配置。
绑定一个类,自动完成配置的读取。
1、NuGet安装:Microsoft.Extensions.Configuration.Binder
2、
static void Main(string[] args)
{
ConfigurationBuilder configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile(
"config.json", optional: false, reloadOnChange: false);
IConfigurationRoot configRoot = configBuilder.Build();
Proxy proxy = configRoot.GetSection("proxy").Get<Proxy>();
Console.WriteLine(proxy.Address);
Console.WriteLine(proxy.Port);
Config config = configRoot.Get<Config>();
Console.WriteLine(config.Name);
Console.WriteLine(config.Proxy.Address);
}
class Config
{
public string Name { get; set; }
public int Age { get; set; }
public Proxy Proxy { get; set; }
}
class Proxy
{
public string Address { get; set; }
public int Port { get; set; }
}