.NET 中的依賴注入(四):生命周期


本文示例代碼,均采用 .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 中使用依賴注入


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM