Microsoft.Extensions.DependencyInjection

ASP.NET Core中,Dependency Injection Container 是一個核心功能,用於管理應用程序中的依賴關係。ASP.NET Core
Dependency Injection Container系統提供了一個建立基於依賴注入的應用程序的機制,並支持以下功能:

  • 提供應用程序組件之間的依賴注入。

  • 可以在Controller、Filter、View等多個組件中使用。

  • 提供了生命週期管理功能,可以在需要時建立、和釋放物件。

  • 支持對服務的多個實現進行注入,並可根據需要在運行時選擇實現。

    .NetCore DI 預設只支援建構子注入,但是Middleware支援方法注入。


IServiceCollection 是什麼?

當查詢原始碼時,會看到下列的介面

1
2
3
4
5
public interface IServiceCollection : IList<ServiceDescriptor>
{


}

ServiceDescriptor.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ServiceDescriptor 
{

/// <inheritdoc />
public ServiceLifetime Lifetime { get; }

/// <inheritdoc />
public Type ServiceType { get; }

/// <inheritdoc />
public Type ImplementationType { get; }

/// <inheritdoc />
public object ImplementationInstance { get; }

/// <inheritdoc />
public Func<IServiceProvider, object> ImplementationFactory { get; }

/// ... 略


}

其實IServiceCollection.cs,其實只是ServiceDescriptor.cs的集合。



Dependency Injection –生命週期

ServiceDescriptor.cs其中的ServiceLifetime.cs 是Enum,主要負責進行依賴注入的物件生命週期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <summary>
/// Specifies the lifetime of a service in an <see cref="IServiceCollection"/>.
/// </summary>
public enum ServiceLifetime
{
/// <summary>
/// Specifies that a single instance of the service will be created.
/// </summary>
Singleton,

/// <summary>
/// Specifies that a new instance of the service will be created for each scope.
/// </summary>
/// <remarks>
/// In ASP.NET Core applications a scope is created around each server request.
/// </remarks>
Scoped,

/// <summary>
/// Specifies that a new instance of the service will be created every time it is requested.
/// </summary>
Transient
}

每種方式的生命週期管理方式都不同,需要根據具體的應用場景來選擇。

  • Singleton:對象在整個應用程序生命週期內只會被創建一次,之後重複使用。
  • Scoped:對象在範圍內只會被建立一次,同一個Request內所有需要使用該對象的Class都共享同一個對象。
  • Transient:對象每次被注入時都會重新創建一個新的Instance。

使用 Dependency Injection 註冊方式

這邊介紹一下在開發上蠻常用的 Dependency Injection 註冊方式

  1. AddXXX<TService,TImplementation>() 加入DI容器並直接決定生命週期。
1
2
3
4
5
6

var builder = WebApplication.CreateBuilder(args);

//建立註冊 生命週期是 Scoped
builder.Services.AddScoped<ISubjectRepository, SubjectRepository>();

  1. AddXXX<TService>(Func<IServiceProvider, object> implementationFactory)
1
2
3
4
5
6
7
8
9

var builder = WebApplication.CreateBuilder(args);

//建立註冊 生命週期是 Scoped
builder.Services.AddScoped<ISubjectRepository>(serviceProvider=>
{
return new SubjectRepository();
});

  1. TryAddXXX<TService,TImplementation>() 加入DI容器,當發現TService已經有註冊了就不再進行註冊。
1
2
3
4
5
6
7
8
9

var builder = WebApplication.CreateBuilder(args);

//建立註冊 生命週期是 Scoped
builder.Services.AddScoped<ISubjectRepository>(serviceProvider=>
{
return new SubjectRepository();
});



多個實作註冊相同的介面

單一介面使用單一實作這邊就不做介紹。多種實作應該是屬常見的情境。
當介面有多個實作時,則會採取最後一個實作。對於 IServiceCollection.cs 是後進先出。但可以取得該介面的所有實作。

1
2
3
4
public interface ISubjectRepository
{
List<Subject> GetAll();
}

在實作部分可能會是DB或是API。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

public class SubjectDbRepository : ISubjectRepository
{
public List<Subject> GetAll()
{
//略
}
}


public class SubjectApiRepository : ISubjectRepository
{
public List<Subject> GetAll()
{
//略
}
}

在DI註冊則是以下程式碼。

1
2
3
4

builder.Services.AddScoped<ISubjectRepository,SubjectDbRepository>();

builder.Services.AddScoped<ISubjectRepository,SubjectApiRepository>();

在這段程式碼中,當使用了 ISubjectRepository.cs ,DI容器會給予 SubjectApiRepository.cs
的實作。但是如果我們需要取得全部可以在建構子中使用 IEnumerable<ISubjectRepository> 的建構子注入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SubjectRepositoryFactory : ISubjectRepositoryFactory
{
private readonly IEnumerable<ISubjectRepository> _subjectRepositories;

public SubjectRepositoryFactory(IEnumerable<ISubjectRepository> subjectRepositories)
{
_subjectRepositories = subjectRepositories;
}

public ISubjectRepository Create()
{
return CreateInstance<SubjectDbRepository>();
}

private ISubjectRepository CreateInstance<T>() where T : ISubjectRepository
{
return this._subjectRepositories.Single(sp => sp is T);
}
}

在這個 SubjectRepositoryFactory.cs 建構子中可以取得 ISubjectRepository.cs 的所有實作。




結論

雖然微軟提供的Dependency Injection 容器很簡單,但是可以應付大部分場景。個人並不喜歡把DI容器使用的深入。不只後續抓錯誤麻煩。也會遇到執行錯誤不知道從哪邊抓。