2013年9月9日 星期一

Unity bootstrapper for ASP.NET MVC 進階註冊類別方式

在「ASP.NET MVC 使用 Autofac」這篇文章裡介紹了另一套 IoC Container 「autofac」於 ASP.NET MVC 的使用方式,而且 autofac 以及一些 IoC Container 都有提供進階的註冊類別方式,可以讓我們在註冊類別的時候可以有比較方便的作法,例如:

image

而 Unity 3.0 也有提供類似這樣的註冊類別的方式,接下來就說明如何使用。

 


以下是安裝並使用 Unity bootstrapper for ASP.NET MVC 的網站專案的 UnityConfig.cs,並沒有使用任何的進階方式,所採用的是最基本的作法,一個一個類別做註冊,

image

這樣的方式並沒有什麼不對,而且程式看起來也沒有多大的問題,但如果專案的 Model 類別變得很多,連帶的 Repository 與 Service 也會跟著變多,此時如果還是用這樣一個一個類別註冊的方式就會蠻累人的。

在 Unity 3.0 之前並沒有提供 RegisterTypes 的方法,不過卻是可以用 RegisterType 方法對泛型類別進行註冊,所以專案的 Repository 為 Generic Type 在進行註冊類別時就可以不用每個 Model 的 Repository 去做註冊的處理,在 Repository 的註冊類別就可以改寫為以下的內容,

image

這邊特別說明 LifetimeManager,Unity bootstrapper for ASP.NET MVC 是使用 Unity 3.0,而 PerRequestLifetimeManager 為 Unity 3.0 所提供的新 LifetimeManager,在 Unity 2.1 以前並沒有提供這種給每次 HttpRequest 使用的 LifetimeManager,因為 Web 的操作是在每一次 HttpRequest 裡進行處理以及完成,所以建立的類別 instance 在每次 HttpRequeest 裡都會是 Singleton,確保不會有重複的 instance 出現。

MSDN - PerRequestLifetimeManager Class (Microsoft.Practices.Unity)

A LifetimeManager that holds onto the instance given to it during the lifetime of a single HTTP request. This lifetime manager enables you to create instances of registered types that behave like singletons within the scope of an HTTP request. See remarks for important usage information.

 

至於 Service 的部份,我們希望可以有自動化註冊 Service 類別的處理,在 Unity 3.0 已經有提供 RegisterTypes 方法,讓我們不用一個一個類別的去註冊了,而 RegisterTypes 為 IUnityContainer 的 Extensions Methods,

MSDN - UnityContainerRegistrationByConventionExtensions Methods (Microsoft.Practices.Unity)

這邊我們要用的是以下的方法,

MSDN - UnityContainerRegistrationByConventionExtensions.RegisterTypes Method (IUnityContainer, IEnumerable(Type), Func(Type, IEnumerable(Type)), Func(Type, String), Func(Type, LifetimeManager), Func(Type, IEnumerable(InjectionMember)), Boolean) (Microsoft.Practices.Unity)

image

看到這個擴充方法的簽名好像挺複雜的,其實實際使用上也還好,因為我們所設計的 Service 還蠻單純的,所以不會有太複雜的設定,修改後的 Service 類別註冊內容,

image

types 使用 AllClasses Class 的 Methods,

MSDN - AllClasses Class (Microsoft.Practices.Unity)

MSDN - AllClasses Methods (Microsoft.Practices.Unity)

我所使用的 AllClasses.FromLoadedAssemblies 方法,預設的參數內容如下:

image

預設是不會對 SystemAssemblies, UniytAssemblies 與 DynamicAssemblies 裡面去找尋要註冊的 Service 類別,如果對於執行效率上有疑慮的話,可以使用 AllClasses.FromAssemblies Method (Boolean, Assembly[]) 指定 Assembly,修改如下:

image

 

getFromTypes 使用 WithMapping class 的 Methods,

MSDN - WithMappings Class (Microsoft.Practices.Unity)

MSDN - WithMappings Methods (Microsoft.Practices.Unity)

 

getNames 使用 WithName class 的 Methods,

MSDN - WithName Members (Microsoft.Practices.Unity)

MSDN - WithName Methods (Microsoft.Practices.Unity)

 

getLifetimeManager 使用 WithLifetime class 的 Methods,

MSDN - WithLifetime Class (Microsoft.Practices.Unity)

MSDN - WithLifetime Methods (Microsoft.Practices.Unity)

因為我們想要使用 PerRequestLifetimeManager,但是 WithLifetime 並沒有提供,

image

不過我們可以使用 WithLifetime.Custom 來完成使用 PerRequestLifetimeManager 的需求,

image

 

修改後的 UnityConfig.RegisterTypes() 如下:

image

不過我們還可以再把程式做個修改,讓程式碼可以更加簡化,

image

 

以上為 Unity bootstrapper for ASP.NET MVC 於設定註冊類別時的進階方式,可以簡化專案的類別註冊處理,並且在日後專案有增加 Model 類別、Repository、Service 的時候就可以讓程式幫我們自動去註冊,而不需要我們自己手動去增加。

前面有說過,因為 Unity bootstrapper for ASP.NET MVC 是使用 Unity 3.0,Unity 3.0 屬於 Enterprise Library 6.0,EntLib 6 僅能安裝在使用 .NET Framework 4.5 的專案上,所以使用 .NET 4.0 或是 .NET 3.5 的 ASP.NET MVC 專案就不能夠使用,而必須使用 Unity.MVC3 或 Unity.MVC4,這兩個並非 EntLib 官方的整合套件,而且是使用 Unity 2.1,所以在使用上就無法像 Unity 3.0 有提供比較進階的方式。

不過還是有不少第三方的整合套件可以幫我們解決這些問題,所以下回再向各位說明 Unity.MVC3 or Unity.MVC4 的進階註冊類別方式。

 

以上

8 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 不好意思,關於某部份有點不懂,所以想要提個疑問。
    (因為下面沒有辦法用大於小於,所以我用大括號取代號,請見諒)
    原本註冊可能是這樣 container.RegisterType{IProductService, ProductService}();
    然後假如我要轉換類別,可能就變成這樣 container.RegisterType{IProductService, ProductTestService}();
    上面的觀念應該是沒錯沒問題的吧??
    那照您的寫法,我有兩個甚至三個類別有實作IProductService的時候,我要怎麼去選擇哪個類別呢???

    回覆刪除
    回覆
    1. Hello, 以下做個簡單說明:
      介面為 IProductService,然後有三個 Service 實作 IProductService
      在使用 Unity 做註冊的時候還是一樣的設定,而在使用端要使用某指定型別的時候,就要另外去做處理.

      //Interface: IProductService
      //Implement: FoodProductService, DrinkProductService, FishProductService

      //以下是在使用端裡的處理,例如在某個 Controller 的建構式裡
      Type serviceType = typeof(FoodProductService);
      this._productService =
      DependencyResolver.Current.GetService(serviceType) as IProductService;

      刪除
    2. 感謝您的解惑,這個就答履了我之前所思考的,如果我要在不同controller呼叫同介面但不同類別的時候,應該要怎麼做,不過這又衍生出了一個問題,不知道您有啥好做法的建議嗎??

      因為如果我在不同的controller用同介面不同類別,如果就像您舉的例有三種的時候,那某天可能某個類別需要改變,我新增了第四種類別,但要去取代當初其中的一種的時候,又回歸老問題了。

      我可能就要去找出多個controller,然後去把那個類別取代成新的........這方面請問有何建議或解決方案的嗎??

      問題有點多,也可能有點淺,請多多見諒。

      刪除
    3. 這個問題不算淺喔,已經是比較進階的操作了,
      關於這個我改天寫一篇文章,或是你可以搜尋有關「strategy pattern + unity」的相關資訊,
      或許有你要的解決方法。

      刪除
    4. 到今天才去研究這部份的議題,我已經實作出來了,不過這種實作方式不知道是否和您思考的方法一樣,還是很期望能看到您分享這種進階的議題,感謝。

      刪除
    5. 話說短時間內我恐怕沒有多餘的時間來針對這主題寫文章了,不過期待你的分享,我也蠻想了解你是如何實作的,謝謝

      刪除
    6. 您好,我最近也很忙,在ios和android和phonegap持續痛苦中,不過我對架構的作法目前和您不同,所以我把unity框架拿掉了,我只有實作service層,然後用您說的strategy填入建構式,但如果我今天要寫repository層的話,我還是會用一個介面對一個類別,因為我覺得您為了不重複程式碼,在repository層其實作法上已經跟原生linq差不多了,這是我的想法,如果有空的話,也請您指教一下。

      刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。