Asp.Net Core中服务的生命周期选项区别与用法详解

2025-05-29 0 104

前言

最近在做一个小的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信息,

第一次启动程序(不关闭)发起访问:

Asp.Net Core中服务的生命周期选项区别与用法详解  

第二次(第一次基础上再次访问)发起访问:

Asp.Net Core中服务的生命周期选项区别与用法详解  

可以看见,两次访问下,Singleton和Instance是相同的,都是由应用程序启动时和应用服务加载时决定完毕,Singleton在首次进入服务时进行分配,并始终保持不变,而Instance在应用程序启动时,便将实例注入,进入服务也保持着最先的实例,没有重新分配实例。而Transient和Scoped则进行着变化。

关闭程序,重启,第三次发起访问:

Asp.Net Core中服务的生命周期选项区别与用法详解  

可以见到,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(),

};

}

在控制器部分调用该聚合服务即可,并返回相应的结果,本次我返回的结果:

Asp.Net Core中服务的生命周期选项区别与用法详解  

可以看到,对于Scoped来讲,一次请求内多次访问同一个服务是共用一个服务实例的,而对于Transient则是,每次访问都是新的服务实例。

至此,对于这四种服务生命周期算是掌握的差不多了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对快网idc的支持。

原文链接:https://www.cnblogs.com/CKExp/p/9823076.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 Asp.Net Core中服务的生命周期选项区别与用法详解 https://www.kuaiidc.com/98584.html

相关文章

发表评论
暂无评论