其他分享
首页 > 其他分享> > .net core 服务生命周期

.net core 服务生命周期

作者:互联网

服务生存期

为每个注册的服务选择适当的生存期。 可以使用以下生存期配置 ASP.NET Core 服务:

暂时

暂时生存期服务 (AddTransient) 是每次从服务容器进行请求时创建的。 这种生存期适合轻量级、 无状态的服务。

范围内

作用域生存期服务 (AddScoped) 以每个客户端请求(连接)一次的方式创建。

 警告

在中间件内使用有作用域的服务时,请将该服务注入至 Invoke 或 InvokeAsync 方法。 请不要通过构造函数注入进行注入,因为它会强制服务的行为与单一实例类似。 有关详细信息,请参阅 写入自定义 ASP.NET Core 中间件

单例

单一实例生存期服务 (AddSingleton) 是在第一次请求时(或者在运行 Startup.ConfigureServices 并且使用服务注册指定实例时)创建的。 每个后续请求都使用相同的实例。 如果应用需要单一实例行为,建议允许服务容器管理服务的生存期。 不要实现单一实例设计模式并提供用户代码来管理对象在类中的生存期。

 警告

从单一实例解析有作用域的服务很危险。 当处理后续请求时,它可能会导致服务处于不正确的状态。

服务注册方法

服务注册扩展方法提供适用于特定场景的重载。

方法自动
对象 (object)
处置
多种
实现
传递参数
Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()
示例:
services.AddSingleton<IMyDep, MyDep>();
No
Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})
示例:
services.AddSingleton<IMyDep>(sp => new MyDep());
services.AddSingleton<IMyDep>(sp => new MyDep("A string!"));
Add{LIFETIME}<{IMPLEMENTATION}>()
示例:
services.AddSingleton<MyDep>();
No
AddSingleton<{SERVICE}>(new {IMPLEMENTATION})
示例:
services.AddSingleton<IMyDep>(new MyDep());
services.AddSingleton<IMyDep>(new MyDep("A string!"));
No
AddSingleton(new {IMPLEMENTATION})
示例:
services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep("A string!"));
No

要详细了解类型处置,请参阅服务处置部分。 多个实现的常见场景是为测试模拟类型

TryAdd{LIFETIME} 方法仅当尚未注册实现时,注册该服务。

在以下示例中,第一行向 IMyDependency 注册 MyDependency。 第二行没有任何作用,因为 IMyDependency 已有一个已注册的实现:

C#
services.AddSingleton<IMyDependency, MyDependency>();
// The following line has no effect:
services.TryAddSingleton<IMyDependency, DifferentDependency>();

有关详细信息,请参见:

TryAddEnumerable(ServiceDescriptor) 方法仅当没有同一类型的实现时,注册该服务。 多个服务通过 IEnumerable<{SERVICE}> 解析。 注册服务时,开发人员只希望在尚未添加一个相同类型时添加实例。 通常情况下,库创建者使用此方法来避免在容器中注册一个实例的两个副本。

在以下示例中,第一行向 IMyDep1 注册 MyDep。 第二行向 IMyDep2 注册 MyDep。 第三行没有任何作用,因为 IMyDep1 已有一个 MyDep 的已注册的实现:

C#
public interface IMyDep1 {}
public interface IMyDep2 {}

public class MyDep : IMyDep1, IMyDep2 {}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep2, MyDep>());
// Two registrations of MyDep for IMyDep1 is avoided by the following line:
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMyDep1, MyDep>());

构造函数注入行为

服务可以通过两种机制来解析:

构造函数可以接受依赖关系注入不提供的参数,但参数必须分配默认值。

当服务由 IServiceProvider 或 ActivatorUtilities 解析时,构造函数注入需要 public 构造函数。

当服务由 ActivatorUtilities 解析时,构造函数注入要求只存在一个适用的构造函数。 支持构造函数重载,但其参数可以全部通过依赖注入来实现的重载只能存在一个。

实体框架上下文

通常使用设置了范围的生存期将实体框架上下文添加到服务容器中,因为 Web 应用数据库操作通常将范围设置为客户端请求。 如果在注册数据库上下文时,AddDbContext<TContext> 重载未指定生存期,则设置默认生存期范围。 给定生存期的服务不应使用生存期比服务短的数据库上下文。

生存期和注册选项

为了演示生存期和注册选项之间的差异,请考虑以下接口,将任务表示为具有唯一标识符 OperationId 的操作。 根据为以下接口配置操作服务的生存期的方式,容器在类请求时提供相同或不同的服务实例:

C#
public interface IOperation
{
    Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}

接口在 Operation 类中实现。 Operation 构造函数将生成一个 GUID(如果未提供):

C#
public class Operation : IOperationTransient, 
    IOperationScoped, 
    IOperationSingleton, 
    IOperationSingletonInstance
{
    public Operation() : this(Guid.NewGuid())
    {
    }

    public Operation(Guid id)
    {
        OperationId = id;
    }

    public Guid OperationId { get; private set; }
}

注册 OperationService 取决于,每个其他 Operation 类型。 当通过依赖关系注入请求 OperationService 时,它将接收每个服务的新实例或基于从属服务的生存期的现有实例。

C#
public class OperationService
{
    public OperationService(
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance instanceOperation)
    {
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = instanceOperation;
    }

    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }
}

在 Startup.ConfigureServices 中,根据其指定的生存期,将每个类型添加到容器中:

C#    
public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    services.AddScoped<IMyDependency, MyDependency>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));

    // OperationService depends on each of the other Operation types.
    services.AddTransient<OperationService, OperationService>();
}

IOperationSingletonInstance 服务正在使用已知 ID 为 Guid.Empty 的特定实例。 此类型在使用时很明显(其 GUID 全部为零)。

示例应用演示了各个请求中和之间的对象生存期。 示例应用的 IndexModel 请求每种 IOperation 类型和 OperationService。 然后,页面通过属性分配显示所有页面模型类和服务的 OperationId 值:

C#      
public class IndexModel : PageModel
{
    private readonly IMyDependency _myDependency;

    public IndexModel(
        IMyDependency myDependency, 
        OperationService operationService,
        IOperationTransient transientOperation,
        IOperationScoped scopedOperation,
        IOperationSingleton singletonOperation,
        IOperationSingletonInstance singletonInstanceOperation)
    {
        _myDependency = myDependency;
        OperationService = operationService;
        TransientOperation = transientOperation;
        ScopedOperation = scopedOperation;
        SingletonOperation = singletonOperation;
        SingletonInstanceOperation = singletonInstanceOperation;
    }

    public OperationService OperationService { get; }
    public IOperationTransient TransientOperation { get; }
    public IOperationScoped ScopedOperation { get; }
    public IOperationSingleton SingletonOperation { get; }
    public IOperationSingletonInstance SingletonInstanceOperation { get; }

    public async Task OnGetAsync()
    {
        await _myDependency.WriteMessage(
            "IndexModel.OnGetAsync created this message.");
    }
}

以下两个输出显示了两个请求的结果:

第一个请求:

控制器操作:

暂时性:d233e165-f417-469b-a866-1cf1935d2518
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

OperationService 操作:

暂时性:c6b049eb-1318-4e31-90f1-eb2dd849ff64
作用域:5d997e2d-55f5-4a64-8388-51c4e3a1ad19
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

第二个请求:

控制器操作:

暂时性:b63bd538-0a37-4ff1-90ba-081c5138dda0
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

OperationService 操作:

暂时性:c4cbacb8-36a2-436d-81c8-8c1b78808aaf
作用域:31e820c5-4834-4d22-83fc-a60118acb9f4
单一实例:01271bc1-9e31-48e7-8f7c-7261b040ded9
实例:00000000-0000-0000-0000-000000000000

观察哪个 OperationId 值会在一个请求之内和不同请求之间变化:

标签:core,生命周期,服务,OperationService,生存期,实例,services,net,public
来源: https://www.cnblogs.com/YzpJason/p/11770407.html