[Tools]IOC控制反轉 & DI依賴注入 & Autofac

IOC(Inversion of Control)控制反轉是物件導向程式設計中的一種設計原則,可以用來減低電腦程式碼之間的耦合度。
依賴注入(Dependency Injection)是最常使用的方式。

IOC(Inversion of Control)

以下用程式碼做簡單的範例說明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ObjectA
{
private ObjectB obj = new ObjectB();

public void FindSomething()
{
obj.FindB();
}
}

public class ObjectB
{
public void FindB()
{
Console.WriteLine("ObjectB FindB");
}
}

ObjectA 使用 ObjectB 的方法去完成一項工作,在 ObjectA 要 new 一個 ObjectB 起來
所以在建立 ObjectA 時,也會把 ObjectB 建立起來
換句話說,現在沒有 ObjectB 就沒有 ObjectA , ObjectA 相依於 ObjectB

如果 ObjectB 需要替換,就必須修改宣告、new 的類型也要修改,可能呼叫的方法也要改,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ObjectA
{
private ObjectC obj = new ObjectC();

public void FindSomething()
{
obj.FindC();
}
}

public class ObjectC
{
public void FindC()
{
Console.WriteLine("ObjectC FindC");
}
}

比較好的設計方式是相依於介面

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
26
27
28
29
30
public interface IObject
{
void Find();
}

public class ObjectA
{
private IObject obj = new ObjectB();

public void FindSomething()
{
obj.Find();
}
}

public class ObjectB : IObject
{
public void Find()
{
Console.WriteLine("ObjectB Find");
}
}

public class ObjectC : IObject
{
public void Find()
{
Console.WriteLine("ObjectC Find");
}
}

DI(Dependency Injection)

這樣在抽換物件時,只需要變更建立類型即可,還可以搭配 DI 讓程式碼更靈活
常見的注入方式有三種:

  • 建構子注入,這是最常見的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObjectA
{
private IObject obj;

public void ObjectA(IObject obj)
{
this.obj = obj;
}

public void FindSomething()
{
obj.Find();
}
}
  • 屬性注入,適合相依物件需要與外部互動
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 ObjectA
{
private IObject obj;

public IObject Obj
{
get
{
return this.obj;
}
set
{
this.obj = value;
}
}

public void FindSomething()
{
if (this.obj == null)
{
throw new ArgumentNullException("obj", "obj is null");
}
obj.Find();
}
}
  • 參數注入
1
2
3
4
5
6
7
8
9
10
11
public class ObjectA
{
public void FindSomething(IObject obj)
{
if (obj == null)
{
throw new ArgumentNullException("obj", "obj is null");
}
obj.Find();
}
}

IOC容器Tool-Autofac

透過NuGet安裝Autofac套件

Autofac套件

依照專案是MVC 或是 WebApi 去安裝整合套件

整合套件

在App_Start資料夾下新增一個AutofacConfig

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
26
27
28
29
30
31
32
33
34
35
36
37
38
namespace WebApi.App_Start
{
using System.Reflection;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;

public static class AutofacConfig
{
public static IContainer Container;

public static void RegisterContainer()
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

builder.RegisterAssemblyTypes(Assembly.Load("WebApi.Domain"), Assembly.Load("WebApi.Persistent"))
.WithParameter("connectionString", ConfigHelper.MasterDB)
.Where(t => t.Namespace == "WebApi.Persistent.SQL" || t.Namespace == "WebApi.Domain.Repository")
.As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == $"I{t.Name}"))
.SingleInstance();

builder.RegisterAssemblyTypes(Assembly.Load("WebApi.Domain"), Assembly.Load("WebApi.Persistent"))
.WithParameter("connectionString", ConfigHelper.MongoDBConnectionString)
.WithParameter("dbName", ConfigHelper.MongoDBName)
.Where(t => t.Namespace == "WebApi.Persistent.Mongo" || t.Namespace == "WebApi.Domain.Repository")
.As(t => t.GetInterfaces().FirstOrDefault(i => i.Name == $"I{t.Name}"))
.SingleInstance();

Container = builder.Build();

var config = GlobalConfiguration.Configuration;
config.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
DependencyResolver.SetResolver(new AutofacDependencyResolver(Container));
}
}
}

在Global的Application_Start啟用Autofac

1
2
3
4
5
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AutofacConfig.RegisterContainer();
}

使用方式

  • 建構子注入
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestController : ApiController
{
private IMemberInfoRepository memberInfoRepo;
public MemberController(IMemberInfoRepository memberInfoRepo)
{
this.memberInfoRepo = memberInfoRepo;
}

public Results GetMemberInfo()
{
var findResult = this.memberInfoRepo.GetMemberInfo();
}
}
  • using
1
2
3
4
5
6
7
8
9
10
11
public class TestController : ApiController
{
public Results GetMemberInfo()
{
using (var scope = App_Start.AutofacConfig.Container.BeginLifetimeScope())
{
var memberInfoRepo = scope.Resolve<IMemberInfoRepository>();
var findResult = memberInfoRepo.GetMemberInfo();
}
}
}

Multiple implementations of an interface for Autofac

https://edo-van-asseldonk.blogspot.com/2013/01/autofac-and-multiple-implementations-of.html

https://autofaccn.readthedocs.io/en/latest/faq/select-by-context.html

https://www.codeproject.com/Tips/870246/How-to-register-and-use-Multiple-Classes-Implement

Multiple implementations of an interface for ASP .NET Core

https://dejanstojanovic.net/aspnet/2018/december/registering-multiple-implementations-of-the-same-interface-in-aspnet-core/

-------------The End-------------