ASP.NET MVC3 Service Location
介紹
ASP.NET MVC3 的一個重要的新特性就是允許注冊一個服務點 Service Location,然后在框架中使用的能力。以前版本的 MVC 已經提供了 Service Location 和依賴注入的機會,在 MVC3 中,我們正式提供了這種能力,并且為開發者開放了多種使用的機會。
總體策略
關于 Service Location 的最重要的部分就是它是可選的,這意味著如果你對 Service Location 不感興趣,那么,你不需要被強制使用,你總是可以在不使用 Service Location 的情況下,調用自定義的函數。在以后加入新的 Service Location 的時候,也會保持向后的兼容性。
當使用注冊的 Service Location 的時候,依賴于具體的使用,MVC 一般將會使用下面三種策略之一:
1. 定位一個唯一的注冊服務
現在的 MVC 中使用的許多服務都注冊一個實例來提供服務,例如,控制器工廠 Controller Factory,它實現了接口 IControllerFactory。對于整個應用來說,只有一個控制器工廠實例。
當 MVC 試圖使用單個的注冊服務的時候,它將會首先通過 Service Locator 尋找時候存在一個注冊的服務實例,如果有,將使用這個服務實例,如果沒有,那么將會回退到沒有使用服務定位器時候注冊的單個實例。
上面的處理順序意味著使用服務定位器的用戶不必擔心現存項目中 MVC 的默認服務。因為當服務提供器不存在的時候,將會自動使用原來注冊的服務,這種潛力也意味著可以在兩個地方注冊自定義的服務,但是只有通過服務器注冊的服務被使用。
2. 定位多個注冊的服務
在 MVC 中也有為一個服務注冊多個服務實例的地方,比如說,視圖引擎,視圖引擎實現了接口 IViewEngine,典型情況下,MVC 提供了注冊多個服務的注冊點,也提供了 ViewEngines.Engines 來找到每一個視圖引擎,并確定其中之一可以提供服務。還有其他的類似的場合,比如, ModelValidatorProviders.Providers 。
當 MVC 試圖使用這種具有多個服務實例的服務的時候,MVC 將會通過管理多個服務實現的 Facade 來完成,Facade 將會把通過靜態注冊的服務實例和通過 Service Location 注冊的服務實例結合在一起來選擇合適的實現。在這里,服務的順序是很重要的,通常意味著通過 Service Location 注冊的服務實例要優先靜態注冊的實例。
類似于單實例服務注冊,這意味著對于存在默認服務提供器的時候,不需要再通過 Service Location 來注冊服務,這也意味著對于注冊的多個服務來說,多數的容器也不必提供一個本地的排序函數,因為如果順序是重要的,就可能需要非 Service Location 的 API。
3. 創建對象
MVC 使用服務定位器最后的策略就是直接創建對象,這最好通過依賴注入的服務來實現,我們將試圖通過 Service Location 來創建對象,一個典型的例子就是控制器對象 Controller。
當 MVC 試圖創建新的對象的時候,它將會請求服務定位器來創建這個對象,如果服務定位器不能完成這個任務,那么,將會回退到 MVC2 的行為,通常意味著使用 Activator.CreateInstance 來創建。
IDependencyResolver
為了在 MVC3 中使用服務定位,在 MVC3 中提供了一個接口 IDependencyResolver 和一個新的類 DependencyResolver,
2 {
3 object GetService( Type serviceType );
4 IEnumerable<object> GetServices( Type serviceType );
5 }
DependencyResolver 的定義如下:
{
public static IDependencyResolver Current { get; }
public static void SetResolver( Object commonServiceLocator );
public static void SetResolver( IDependencyResolver resolver );
public static void SetResolver( Func<Type, Object> getService, Func<Type, Enumerable<Object>> getServices );
}
三個靜態方法提供了注冊服務定位器的機制,靜態屬性 Current 用來獲取注冊的服務點。
Common Service Locator 提供了一個可以在應用程序或者框架中使用的共享的服務點,同時,項目中也提供了對于常見 IoC 的包裝。
Service locator adapter implementations
Implementation |
---|
Castle Windsor Adapter |
Spring .NET Adapter |
Unity Adapter |
StructureMap Adapter |
Autofac Adapter |
MEF Adapter now on .NET Framework 4.0 |
LinFu Adapter |
Multi-target CSL binaries |
示例
例如,在程序中我們定義了如下的接口用于獲取信息。并提供了兩種實現方式:Hello 和 Hi。
{
public interface IHello
{
string Message { get; }
}
public class Hello : IHello
{
public string Message
{
get { return "Hello, world."; }
}
}
public class Hi : IHello
{
public string Message
{
get { return "世界,你好!"; }
}
}
}
項目中使用 Unity 來作為實際的 IoC 容器。類型注冊通過配置文件完成。
<configSections>
<section
name="unity"
type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<typeAliases>
<typeAlias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager, Microsoft.Practices.Unity" />
<typeAlias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager, Microsoft.Practices.Unity" />
<typeAlias alias="perThread" type="Microsoft.Practices.Unity.PerThreadLifetimeManager, Microsoft.Practices.Unity" />
</typeAliases>
<containers>
<container>
<types>
<type type="mvc_1.study.IHello,StudyModel"
mapTo="mvc_1.study.Hi,StudyModel" >
<lifetime type="singleton" />
</type>
</types>
</container>
</containers>
</unity>
<configuration>
在程序中,我們可以使用類似于下面的代碼來注冊服務點。
IUnityContainer container = new Microsoft.Practices.Unity.UnityContainer();
// 讀取配置文件
Microsoft.Practices.Unity.Configuration.UnityConfigurationSection
config
= System.Configuration.ConfigurationManager
.GetSection("unity")
as UnityConfigurationSection;
// 通過配置文件設置容器
config.Configure(container);
// 在代碼中直接注冊,一般不用
// container.RegisterType<mvc_1.study.IHello, mvc_1.study.Hello>();
// 包裝為 ServiceLocator
Microsoft.Practices.Unity.ServiceLocatorAdapter.UnityServiceLocator locator
= new Microsoft.Practices.Unity.ServiceLocatorAdapter.UnityServiceLocator(container);
// 注冊
DependencyResolver.SetResolver(locator);
在代碼中,可以直接通過服務點來獲取注冊的對象實例。
= DependencyResolver.Current.GetService<mvc_1.study.IHello>();
關于 unity 容器,可以參考
Enterprise Library - Unity Application Block 學習手冊(最新版) Part 1