前言
最近在做一个小的Demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,寻找一番,发现是配置依赖注入服务时,对于服务的生命周期没有配置得当导致,特此做一次实验来认识三者之间(甚至是四者之间的用法及区别)。
本文demo地址(具体见WebApi控制器中):https://gitee.com/530521314/koInstance.git
在Asp.Net Core中,内置容器负责管理服务的生命周期,从被依赖注入容器创建开始,等我们调用完服务时,到容器释放该服务的所有实力为止,有几种形式表现:
1、Transient:每次请求服务时,都会创建一个新实例,这种生命周期适合用于轻量级服务(如Repository和ApplicationService服务)。
2、Scoped:为每个HTTP请求创建一个实例,生命周期将横贯整次请求。
3、SingleTon:在第一次请求服务时,为该服务创建一个实例,之后每次请求将会使用第一次创建好的服务。
4、Instance:与SingleTon类似,但在应用程序启动时会将该实例注册到容器中,可以理解为比SingleTon还早存在。
应用程序中相关服务的控制生命周期的方法时通过相应的Add*指定,如下三种,当然还可以通过扩展方法来简化ConfigurationServices方法中所见的代码数量。
|
1
2
3
|
services.AddTransient<IApplicationService, ApplicationService>();
services.AddScoped<IApplicationService, ApplicationService>();
services.AddSingleton<IApplicationService, ApplicationService>();
|
首先设计一些服务相关的操作接口
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public interface IOperation
{
Guid GetGuid();
}
public interface IOperationTransient: IOperation
{
}
public interface IOperationScoped : IOperation
{
}
public interface IOperationSingleton : IOperation
{
}
public interface IOperationInstance : IOperation
{
}
基础服务接口
|
其次对这些操作类予以实现并生成相关服务
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
/// <summary>
/// 常规服务
/// </summary>
public class Operation : IOperation
{
private readonly Guid _guid;
public Operation()
{
_guid = Guid.NewGuid();
}
public Operation(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 瞬时服务
/// </summary>
public class OperationTransient : IOperationTransient
{
private readonly Guid _guid;
public OperationTransient()
{
_guid = Guid.NewGuid();
}
public OperationTransient(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 单次请求内服务固定
/// </summary>
public class OperationScoped : IOperationScoped
{
private readonly Guid _guid;
public OperationScoped()
{
_guid = Guid.NewGuid();
}
public OperationScoped(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 所有请求内固定服务
/// </summary>
public class OperationSingleton : IOperationSingleton
{
private readonly Guid _guid;
public OperationSingleton()
{
_guid = Guid.NewGuid();
}
public OperationSingleton(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
/// <summary>
/// 应用程序内固定服务
/// </summary>
public class OperationInstance : IOperationInstance
{
private readonly Guid _guid;
public OperationInstance()
{
_guid = Guid.NewGuid();
}
public OperationInstance(Guid guid)
{
_guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
}
public Guid GetGuid()
{
return _guid;
}
}
基础服务具体实现
|
|
1
2
3
4
5
6
7
8
9
10
|
public interface IOperationService
{
/// <summary>
/// 获取四种形式的Guid码
/// </summary>
/// <returns></returns>
List<string> GetGuidString();
}
聚合服务接口
|
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
/// <summary>
/// 服务调用
/// </summary>
public class OperationService : IOperationService
{
public IOperationTransient _transientOperation { get; }
public IOperationScoped _scopedOperation { get; }
public IOperationSingleton _singletonOperation { get; }
public IOperationInstance _instanceOperation { get; }
public OperationService(IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation,
IOperationInstance instanceOperation)
{
_transientOperation = transientOperation;
_scopedOperation = scopedOperation;
_singletonOperation = singletonOperation;
_instanceOperation = instanceOperation;
}
public List<string> GetGuidString()
{
return new List<string>()
{
$"Transient:"+_transientOperation.GetGuid(),
$"Scoped:"+_scopedOperation.GetGuid(),
$"Singleton:" +_singletonOperation.GetGuid(),
$"Instance:"+_instanceOperation.GetGuid(),
};
}
}
聚合服务的实现
|
在控制器中进行服务注入
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IOperationService _operationService;
public ValuesController(IOperationService operationService)
{
_operationService = operationService;
}
[HttpGet]
[Route(nameof(GetGuidString))]
public ActionResult<string> GetGuidString()
{
return string.Join("\\n", _operationService.GetGuidString());
}
}
|
在StartUp中完成服务注入逻辑,这里实现服务注入的方式多种均可。
|
1
2
3
4
5
|
services.AddTransient<IOperationTransient, OperationTransient>();
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddSingleton<IOperationSingleton, OperationSingleton>();//应用程序启动时便注入该实例
services.AddSingleton<IOperationInstance>(new OperationInstance(Guid.Empty));
services.AddTransient<IOperationService, OperationService>();
|
通过访问预期Api地址可以得到不同的四种基础服务的Guid信息,
第一次启动程序(不关闭)发起访问:
第二次(第一次基础上再次访问)发起访问:
可以看见,两次访问下,Singleton和Instance是相同的,都是由应用程序启动时和应用服务加载时决定完毕,Singleton在首次进入服务时进行分配,并始终保持不变,而Instance在应用程序启动时,便将实例注入,进入服务也保持着最先的实例,没有重新分配实例。而Transient和Scoped则进行着变化。
关闭程序,重启,第三次发起访问:
可以见到,Singleton和Instance都发生了变化,也说明了之前在Singleton和Instance处写上的作用。
接下来开始设计Transient和Scoped的不同之处,对于已有代码加上新功能,此次我们只针对Scoped和Transient进行比较。
首先在StartUp中将HttpContextAccessor服务注入,目的是在后期能够针对Scoped获取新的服务实例(尽管两个实例是相同的)。
|
1
|
services.AddHttpContextAccessor();
|
接着在聚合服务中增加一个方法,用来针对Transient、Scoped测试。
|
1
2
3
4
5
|
/// <summary>
/// 获取Transient、Scoped的Guid码
/// </summary>
/// <returns></returns>
List<string> GetTransientAndScopedGuidString();
|
在聚合服务实现中实现该方法并对已有的服务重新获取实例,得到不同实例下的Guid码。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public List<string> GetTransientAndScopedGuidString()
{
//var tempTransientService = (IOperationTransient)ServiceLocator.Instance.GetService(typeof(IOperationTransient));
var tempTransientService = (IOperationTransient)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationTransient));
var tempScopedService = (IOperationScoped)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationScoped));
return new List<string>()
{
$"原生Transient请求服务:"+_transientOperation.GetGuid(),
$"手动Transient请求服务:"+ tempTransientService.GetGuid(),
$"原生Scoped请求服务:"+_scopedOperation.GetGuid(),
$"手动Scoped请求服务:"+tempScopedService.GetGuid(),
};
}
|
在控制器部分调用该聚合服务即可,并返回相应的结果,本次我返回的结果:
可以看到,对于Scoped来讲,一次请求内多次访问同一个服务是共用一个服务实例的,而对于Transient则是,每次访问都是新的服务实例。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对快网idc的支持。
原文链接:https://www.cnblogs.com/CKExp/p/9823076.html
相关文章
- 个人网站搭建:如何挑选具有弹性扩展能力的服务器? 2025-06-10
- 个人服务器网站搭建:如何选择适合自己的建站程序或框架? 2025-06-10
- 64M VPS建站:能否支持高流量网站运行? 2025-06-10
- 64M VPS建站:怎样选择合适的域名和SSL证书? 2025-06-10
- 64M VPS建站:怎样优化以提高网站加载速度? 2025-06-10
- 2025-07-10 怎样使用阿里云的安全工具进行服务器漏洞扫描和修复?
- 2025-07-10 怎样使用命令行工具优化Linux云服务器的Ping性能?
- 2025-07-10 怎样使用Xshell连接华为云服务器,实现高效远程管理?
- 2025-07-10 怎样利用云服务器D盘搭建稳定、高效的网站托管环境?
- 2025-07-10 怎样使用阿里云的安全组功能来增强服务器防火墙的安全性?
快网idc优惠网
QQ交流群
-
2025-05-29 93
-
2025-05-27 29
-
2025-05-29 58
-
2025-05-25 37
-
PHP中new static()与new self()的比较
2025-05-29 18





