🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

Asp.net MVC如何實現IOC解析器 (第13天)

Agenda

前言

IOC依賴反轉是oop重要程式設計思想。

Ioc—Inversion of Control 控制反轉

控制反轉是一個設計思想 ,把對於某個物件的控制權移轉給第三方容器.

詳細資訊可以查看小弟另一篇文章 IOC(控制反轉),DI(依賴注入) 深入淺出~~

有沒有人會很好奇說為什麼只需要透過DependencyResolver.SetResolver方法我就可以直接使用AutoFac或其他IOC容器?

1
2
3
4
//....
// 建立相依解析器
IContainer container = new builder.Build();
DependencyResolver.SetResolver(container);

今天跟大家分享Asp.net MVC利用什麼設計技巧,讓外部IOC容器可以很方便融入系統中.

我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.

IOC介紹

控制反轉是一個設計思想,把對於某個物件建立,生命週期控制權移轉給第三方統一管理
在設計模組時建議依賴抽象,因為各個模組間不需要知道對方太多細節(實作),知道越多耦合越強。

A物件內部有使用到B物件 A,B物件中有依賴的成份
控制反轉是把原本AB控制權移交給第三方容器。
降低AB物件的耦合性,讓雙方都倚賴第三方容器。

上面說明太抽象嗎? 可以看一下下面這張圖.

img

最後對於使用者來說,我只需要認識這個第三方容器並跟這個容器取得我要A物件,至於A物件和其他物件關係使用者不用瞭解

IOC容器框架有很多種但基本上都有下面兩個功能

  1. 掌控物件生命週期
  2. 設定物件關係的註冊表(取用時會依照此註冊關係建立物件並自動注入相依物件)

程式碼介紹IOC by Autofac

我們依照此圖做一個簡單範例by Autofac

img

A物件會直接引用於BC物件這導致A掌控BC物件創建和銷毀

如下面程式碼,A物件需要掌控BC生命週期和物件建立.

1
2
3
4
public class A{
public B BObject {get;set;} = new B();
public C CObject {get;set;} = new C();
}

如果透過IOC容器我們就不用擔心物件如何建立和他所依賴BC物件,因為我們會在容器註表中指定他的關係,使用時只需要關注如何使用此物件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class A{
public B BObject {get;private set;}
public C CObject {get;private set;}
public A(B b,C c){
BObject = b;
CObject = c;
}
}

//autofac property injection
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<B>();
builder.RegisterType<C>();
builder.RegisterType<A>().PropertiesAutowired();
IContainer container = builder.Build();

var a = container.Resolve<A>();

這個程式碼是利用Autofac框架,比起上面多了一段註冊程式碼.主要告訴容器物件之間關係和如何掌控物件生命週期.

上面例子最後只需要利用container.Resolve<T>方法就可以跟容器來取想要的物件,至於引用的物件是如何注入或關係我們就不必關心.

AutoFac IOC容器 和 Asp.net mvc關係

如果Asp.net沒有搭配IOC容器(預設使用DefaultResolver)Asp.net MVC對於使用物件必須寫死在Controller類別中

無法使用建構子或屬性來決定使用哪個物件

如下面程式碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HomeController : Controller
{
IUserService userService;

public HomeController(IUserService userService){
if(userService == null)
userService = new UserService();
}
public ActionResult Index()
{

return View();
}
//....

如果在建構子使用參數會丟錯誤,在[Day11] Asp.net MVC Controller是怎麼被建立談到建立Controller物件透過DefaultControllerActivator預設使用反射建立Controller物件呼叫無參數的建構子方法.

relationship_pic.PNG

因為Asp.net MVC建立Controller是透過Activator.CreateInstance方法,

如果我們想在建構子傳入參數或是想要統一管理注入的物件,就可以使用IOC容器來幫我完成


為什麼Asp.net MVC使用DependencyResolver.SetResolver方法替換成IOC容器就可輕易替換使用容器?

1
2
3
4
//....
// 建立相依解析器
IContainer container = new builder.Build();
DependencyResolver.SetResolver(container);

DependencyResolver 揭密

DependencyResolver.SetResolver提供一個替換_current欄位的機制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// 可將第三方IOC容器設置
/// </summary>
/// <param name="resolver"></param>
public static void SetResolver(IDependencyResolver resolver)
{
_instance.InnerSetResolver(resolver);
}

public static void SetResolver(object commonServiceLocator)
{
_instance.InnerSetResolver(commonServiceLocator);
}

public void InnerSetResolver(IDependencyResolver resolver)
{
if (resolver == null)
{
throw new ArgumentNullException("resolver");
}

_current = resolver;
_currentCache = new CacheDependencyResolver(_current);
}

Asp.net MVC 提供一個介面 IDependencyResolver 讓第三方容器實現並擴充.
IDependencyResolver介面有兩個方法

  1. GetService返回一個物件
  2. GetServices返回一個物件集合

主要透過這GetService方法取得使用Controller物件

1
2
3
4
5
public interface IDependencyResolver
{
object GetService(Type serviceType);
IEnumerable<object> GetServices(Type serviceType);
}

MVC 裡IDependencyResolver

Asp.net MVC依賴DependencyResolver.Current來幫我們建立一個Controller物件

這邊介紹一下在MVC中三個IDependencyResolver解析器

  1. CacheDependencyResolver 快取解析器(利用ConcurrentDictionary是一個多執行緒安全的字典)
  2. DefaultDependencyResolver預設使用解析器(利用反射建立物件)
  3. DelegateBasedDependencyResolver委派解析器.
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
prprivate sealed class CacheDependencyResolver : IDependencyResolver
{
//ConcurrentDictionary 是一個多執行緒 安全的Dictionary
private readonly ConcurrentDictionary<Type, object> _cache = new ConcurrentDictionary<Type, object>();

private readonly ConcurrentDictionary<Type, IEnumerable<object>> _cacheMultiple = new ConcurrentDictionary<Type, IEnumerable<object>>();
private readonly Func<Type, object> _getServiceDelegate;
private readonly Func<Type, IEnumerable<object>> _getServicesDelegate;

private readonly IDependencyResolver _resolver;

public CacheDependencyResolver(IDependencyResolver resolver)
{
_resolver = resolver;
_getServiceDelegate = _resolver.GetService;
_getServicesDelegate = _resolver.GetServices;
}

public object GetService(Type serviceType)
{
return _cache.GetOrAdd(serviceType, _getServiceDelegate);
}

public IEnumerable<object> GetServices(Type serviceType)
{
return _cacheMultiple.GetOrAdd(serviceType, _getServicesDelegate);
}
}

private class DefaultDependencyResolver : IDependencyResolver
{
public object GetService(Type serviceType)
{
// Since attempting to create an instance of an interface or an abstract type results in an exception, immediately return null
// to improve performance and the debugging experience with first-chance exceptions enabled.
if (serviceType.IsInterface || serviceType.IsAbstract)
{
return null;
}

try
{
return Activator.CreateInstance(serviceType);
}
catch
{
return null;
}
}

public IEnumerable<object> GetServices(Type serviceType)
{
return Enumerable.Empty<object>();
}
}

建立Controller預設使用DefaultDependencyResolver這個解析器

第三方IOC容器利用DependencyResolver.SetResolver方法把DefaultDependencyResolver替換掉使用他們自己實現的解析器提供物件

不是透過DefaultDependencyResolver反射來建立物件喔~

小結:

我們了解為什麼Asp.net MVC可透過DependencyResolver.SetResolver替換成IOC容器注入控制器物件.

如果要建立客製化的解析器可以實現IDependencyResolver介面並使用DependencyResolver.SetResolver替換DefaultDependencyResolver預設解析器

DependencyResolver,ControllerControllerFactory的關係如下圖

IOC_Asp.netMVC.png

下篇會介紹DependencyResolverAsp.net MVC中有哪些實際的應用.

__此文作者__:Daniel Shih(石頭)
__此文地址__: https://isdaniel.github.io/ithelp-day13/
__版權聲明__:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!

如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^