🚫 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%

動手DIY改造 Asp.net MVC- 自己動作建立一個DependencyResolver解析器(Autofac) (第27天)

Agenda

前言

產生Controller物件相關物件關係如下面UML圖

relationship_pic.PNG

透過ControllerFactory建立一個Controller控制器物件.而ControllerFactory依賴IControllerActivator物件產生Controller.

上面IControllerActivator可以透過建立使用我們的依賴注入容器來替換原本反射產生物件.

DependencyResolverMVC提供的一個可替換物注入點,今天我們會藉由他來我們實現注入MVC方式.

Aufofac依賴注入容器

在實現自己的DependencyResolver前先談談Autofac容器做甚麼用的?

我之前有寫一篇IOC(控制反轉),DI(依賴注入) 深入淺出~~,講述**IOC(控制反轉),DI(依賴注入)**這兩個設計技巧的理念核心.

言簡意賅可以統一交由容器來幫忙管理物件生命週期和建立方式,也管理物件相依性,兩個重點我們使得只需要提供使用類別的特徵(型別或其他可辨別特徵),容器就提供給我們相對應的物件.

Autofac有需多使用方式這裡就不一一介紹,有興趣讀者可以上網google或是查閱Autofac官方文件

IDependencyResolver介面

DependencyResolver是一個靜態物件,MVC application使用同一個解析器(DefaultDependencyResolver)而他有一個SetResolver方法可以替換成其他DependencyResolver

IDependencyResolver有兩個方法需要實現.

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

MVC依賴於GetServiceGetServices,取得物件實例並提供一個抽象提供外部提供修改或擴充.

預設使用(DefaultDependencyResolver)這個解析器來取得我們物件(DefaultDependencyResolver解析器使用Activator.CreateInstance(serviceType);建立物件)

建立CustomerDependencyResolver(IDependencyResolver)

這邊我們利用autofac來完成建立物件動作,先建立一個ILifetimeScope _container由建構子注入此物件.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CustomerDependencyResolver : IDependencyResolver
{
private readonly ILifetimeScope _container;

public CustomerDependencyResolver(ILifetimeScope container)
{
if (container == null)
throw new ArgumentNullException(nameof (container));

_container = container;
}

public object GetService(Type serviceType)
{
return _container.ResolveOptional(serviceType);
}

public IEnumerable<object> GetServices(Type serviceType)
{
return (IEnumerable<object>) _container.ResolveOptional(typeof (IEnumerable<>).MakeGenericType(serviceType));
}
}

GetService呼叫ResolveOptional方法透過Type到容器中搜尋匹配的物件並返回.

CustomerControllerActivator(IControllerActivator)

IControllerActivator有一個Create方法,ControllerFacotry靠它來幫我們產生使用Controller物件,而我們在這邊建立自己IControllerActivator並在Create方法中實現自己得邏輯.透過DependencyResolver來產生物件(替換成CustomerDependencyResolver)

1
2
3
4
5
6
7
public class CustomerControllerActivator : IControllerActivator
{
public IController Create(RequestContext requestContext, Type controllerType)
{
return (IController) DependencyResolver.Current.GetService(controllerType);
}
}

我們會在Autofac容器註冊目前Assembly所有繼承IController物件.

1
2
//注入typeof(MvcApplication).Assembly 中所有繼承IController物件.
builder.RegisterControllers(typeof(MvcApplication).Assembly);

在上面CustomerControllerActivator.Create會透Autofac解析器幫我們建立Controller

在Application_Start中MVC替換成自己的解析器

  1. 首先利用ControllerBuilderSetControllerFactory方法,重新替換使用ControllerFacotry.

  2. 在利用builder.RegisterControllers注入typeof(MvcApplication).Assembly中所有繼承IController物件.

  3. 註冊IMemberService介面物件(裡面有一個int GetMemberBalance(int memberId);方法來模擬取得會員餘額)

  4. DependencyResolver.SetResolver(new CustomerDependencyResolver(builder.Build()))替換成我們使用的解析器

因為ControllerFacotry預設使用DefaultControllerActivator,而我們需要替換成自己建立得CustomerControllerActivator並利用容器來幫我們注入.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//把DefaultControllerFactory 中的IControllerActivator替換成我們自己寫的CustomerControllerActivator
ControllerBuilder.Current.SetControllerFactory(
new DefaultControllerFactory(new CustomerControllerActivator()));
AutofacRegister();
}

private static void AutofacRegister()
{
ContainerBuilder builder = new ContainerBuilder();
//注入typeof(MvcApplication).Assembly 中所有繼承IController物件.
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<MemberService>().As<IMemberService>();
//替換成自己的DependencyResolver
DependencyResolver.SetResolver(new CustomerDependencyResolver(builder.Build()));
}
}

在Controller使用注入

HomeController控制器中在建構子注入,並呼叫IMemberService.GetMemberBalance方法

執行專案請求Home/About頁面可以看到ViewBag.Message已經成功顯示一個HardCode餘額了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HomeController : Controller
{
private readonly IMemberService _service;

public HomeController(IMemberService service)
{
_service = service;
}

public ActionResult About()
{
ViewBag.Message = $"Member Balance { _service.GetMemberBalance(123)}";

return View();
}
}

小結:

DefaultControllerActivator使用反射建立一個Controller物件

然而IControllerActivator提供一個產生Controller接口,而我們可以藉由實現此介面並使用DependencyResolver靜態物件產生Controller物件(藉由容器框架產生).

最後會把Controller依賴物件藉由依賴注入容器注入進去.

Github範例程式原始碼 CustomerContainer分支上

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

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