Alva

Alva

programmer
github
telegram
email

dotnet 中的依賴注入-04.生命週期

本文示例代码,均采用 .NET 6,具体的代码可以在这个仓库 Articles.DI 中获取。

前面的文章中,在註冊服務時,統一使用了services.AddSingleton<TService, TImplementation>()的形式來註冊服務,這個方法的具體含義是什麼?有沒有其他類似的方法?而且我們還有一個疑問,容器在構造服務時,服務的生命週期是怎麼樣的?服務被申請一次,就構造一次嗎?還是整個程序週期,只構造一次?如果我們要自定義每個服務有不同的生命週期時,又該怎麼做?下面,我們將一起探尋服務的生命週期。

生命週期#

註冊到容器時,服務的生命週期分為三種

生命週期何時創建調用方法
瞬時 / Transient每次請求,都会創建一個新的實例AddTransient()
範圍 / Scoped在指定的範圍內,第一次請求時會創建一個實例
重複請求時,會返回同一個實例
AddScoped()
單例 / Singleton在整個程序生命週期,只會創建一次
後續所有請求,都会返回同一個實例
AddSingleton()

參考實際代碼,我們可以更直觀的理解依賴注入中的三種生命週期,下面的代碼可以在微軟的 .NET 文檔中找到對應的文章 ——教程:在 .NET 中使用依賴注入。或者可以在這個倉庫中直接查看獲取源碼

// https://github.com/alva-lin/Articles.DI/tree/master/ConsoleApp1
// 01. 聲明接口以及實現類
public interface IOperation
{
    string OperationId { get; }
}

public interface ITransientOperation : IOperation {}

public interface IScopedOperation : IOperation {}

public interface ISingletonOperation : IOperation {}

public class DefaultOperation
    : ITransientOperation, IScopedOperation, ISingletonOperation
{
    // 創建一個 Guid,取最後 4 個字符
    public string OperationId { get; } = Guid.NewGuid().ToString()[^4..];
}
// 02. 創建一個服務,它依賴上面幾個 Operation
public class OperationLogger
{
    private readonly ITransientOperation _transientOperation;
    private readonly IScopedOperation    _scopedOperation;
    private readonly ISingletonOperation _singletonOperation;

    public OperationLogger(ITransientOperation transientOperation,
                           IScopedOperation    scopedOperation,
                           ISingletonOperation singletonOperation)
    {
        _transientOperation = transientOperation;
        _scopedOperation    = scopedOperation;
        _singletonOperation = singletonOperation;
    }

    public void LogOperations(string scope)
    {
        LogOperation(_transientOperation, scope, "Always different");
        LogOperation(_scopedOperation, scope, "Changes only with scope");
        LogOperation(_singletonOperation, scope, "Always the same");
    }

    private static void LogOperation<T>(T operation, string scope, string message)
        where T : IOperation
    {
        Console.WriteLine($"{scope}: {typeof(T).Name,-19} [{operation.OperationId} {message,-23}]");
    }
}
// 03. 註冊服務並啟動程序
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateDefaultBuilder(args)
   .ConfigureServices(services =>
    {
        // 將三種 Operation 分別註冊為三種聲明週期
        services.AddTransient<OperationLogger>()
           .AddTransient<ITransientOperation, DefaultOperation>()
           .AddScoped<IScopedOperation, DefaultOperation>()
           .AddSingleton<ISingletonOperation, DefaultOperation>();
    })
   .Build();

ExemplifyScoping(host.Services, "Scope 1");
ExemplifyScoping(host.Services, "Scope 2");

await host.RunAsync();


static void ExemplifyScoping(IServiceProvider services, string scope)
{
    using IServiceScope serviceScope = services.CreateScope();
    IServiceProvider    provider     = serviceScope.ServiceProvider;

    var logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations($"{scope}: Call 1 ...");
    Console.WriteLine();

    logger = provider.GetRequiredService<OperationLogger>();
    logger.LogOperations($"{scope}: Call 2 ...");
    Console.WriteLine();
}

運行上述代碼,可以獲得下面的結果

Scope 1: Call 1 ...: ITransientOperation [b672 Always different       ]
Scope 1: Call 1 ...: IScopedOperation    [afd8 Changes only with scope]
Scope 1: Call 1 ...: ISingletonOperation [21b3 Always the same        ]

Scope 1: Call 2 ...: ITransientOperation [b6fc Always different       ]
Scope 1: Call 2 ...: IScopedOperation    [afd8 Changes only with scope]
Scope 1: Call 2 ...: ISingletonOperation [21b3 Always the same        ]

Scope 2: Call 1 ...: ITransientOperation [ef31 Always different       ]
Scope 2: Call 1 ...: IScopedOperation    [46d1 Changes only with scope]
Scope 2: Call 1 ...: ISingletonOperation [21b3 Always the same        ]

Scope 2: Call 2 ...: ITransientOperation [9864 Always different       ]
Scope 2: Call 2 ...: IScopedOperation    [46d1 Changes only with scope]
Scope 2: Call 2 ...: ISingletonOperation [21b3 Always the same        ]

比對程序輸出的日誌,可以看出,每個ITransientOperation的輸出都會不一樣;而IScopedOperation在相同的範圍內輸出內容一樣,在不同的範圍間輸出內容不同;最後一個ISingletonOperation,每次的輸出都是相同的。這樣的輸出結果,也符合我們前面的表格展示內容。

參考鏈接#

.NET 中的依賴關係注入

教程:在 .NET 中使用依賴注入

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。