[C#][DI] 相互依賴造成循環注入死結

問題緣由

最近在寫 .NET Core 的時候遇到了一個問題

1
A circular dependency was detected for the service of type 'WebApplication1.Service.A1Service'

A1Service

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
public class A1Service
{
private readonly string guid = null;
private readonly A2Service _a2Service = null;

public A1Service(A2Service a2Service)
{
_a2Service = a2Service;

if (guid == null)
{
guid = Guid.NewGuid().ToString();
}
}

public void Run()
{
Console.WriteLine("A1Service: " + guid);
}

public void RunOther()
{
_a2Service.Run();
}
}

這是網路教學文章上常見的從建構子中依賴注入 A2Service 的程式
接著我們再來看看另外一支程式

A2Service

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
public class A2Service
{
private readonly string guid = null;
private readonly A1Service _a1Service = null;

public A2Service(A1Service a1Service)
{
_a1Service = a1Service;

if (guid == null)
{
guid = Guid.NewGuid().ToString();
}
}

public void Run()
{
Console.WriteLine("A2Service: " + guid);
}

public void RunOther()
{
_a1Service.Run();
}
}

這裡就發生了一個循環依賴的問題 A1Service 在建構子中注入了 A2Service,而 A2Service 也在建構子中注入了 A1Service,造成兩個物件互相依賴造成 Exception

解決方法

改成不直接在建構子中注入 Service ,改注入 IServiceProvider,再利用 GetService 這個方法來取得物件,這樣就可以順利解決這個問題囉。

A3Service

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
public class A3Service
{
private readonly string guid = null;
private readonly IServiceProvider _serviceProvider = null;

public A3Service(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;

if (guid == null)
{
guid = Guid.NewGuid().ToString();
}
}

public void Run()
{
Console.WriteLine("A3Service: " + guid);
}

public void RunOther()
{
_serviceProvider.GetService<A4Service>().Run();
}
}

A4Service

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
public class A4Service
{
private readonly string guid = null;
private readonly IServiceProvider _serviceProvider = null;

public A4Service(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;

if (guid == null)
{
guid = Guid.NewGuid().ToString();
}
}

public void Run()
{
Console.WriteLine("A4Service: " + guid);
}

public void RunOther()
{
_serviceProvider.GetService<A3Service>().Run();
}
}