ASP.NET MVC 示例項目:Suteki.Shop
在這個ASP.NET MVC 示例:Suteki.Shop中,未使用微軟自已的Unity框架來實現(xiàn)IOC,而是使用了大名鼎鼎Castle Windsor。因為引用了Windsor,就有必要簡要介紹一下。而我的理解,這個IOC容器(Container)包括下面幾個重要概念:
容器(Container):Windsor是一個反轉(zhuǎn)控制容器。它創(chuàng)建在一個微內(nèi)核的基礎(chǔ)之上,這個微內(nèi)
核能夠掃描類并且試圖找到這些類用到哪些對象引用、對象依賴,然后把這些依賴信息提供給類使用。
組件(Component):也就是我們通常所說的業(yè)務(wù)邏輯單元及相應(yīng)的功能實現(xiàn),組件是一個可復
用的代碼單元。它應(yīng)該實現(xiàn)并暴露為一個服務(wù)。組件是實現(xiàn)一個服務(wù)或接口的類。
服務(wù)(Service) :也就是相應(yīng)的組件接口或N個Component按業(yè)務(wù)邏輯組合而成的業(yè)務(wù)邏輯接口。
接口是服務(wù)的規(guī)范,它創(chuàng)建一個抽象層,你可以輕松的替換服務(wù)的實現(xiàn)。
擴張單元插件(Facilities):提供(可擴張)容器以管理組件。
我們可以直接使用組件(會在下面的內(nèi)容中提到),也可以把組件轉(zhuǎn)換成相應(yīng)的服務(wù)接口來使用。
還記得上一篇文章中提到的Service嗎? 說白了,它就是一個服務(wù)。而Suteki.Shop做的更“夸張”,要是帶有業(yè)務(wù)邏輯性質(zhì)的功能代碼都可以被視為Component或服務(wù),比如說前幾篇文章中所提到的Filter,ModelBinder。甚至是服務(wù)組件初始化的輔助類(WindsorServiceLocator)也一并拿下。
為了便于理解,下面就到Suteki.Shop中看一下其是如何做的
首先我們看一下整個Suteki.Shop項目啟動的入口,同時這也是Windsor IOC容器初始化的起點。而這塊功能代碼是放在了Global.asax(Suteki.Shop\Global.asax)中的Application_Start方法中實現(xiàn)的,下面是該方法的聲明:
ASP.NET MVC 示例代碼
- protected void Application_Start(object sender, EventArgs e)
- {
- RouteManager.RegisterRoutes(RouteTable.Routes);
- InitializeWindsor();
- }
代碼中的RouteManager.RegisterRoutes是實現(xiàn)對Route規(guī)則的綁定,而規(guī)則的內(nèi)容是被硬編碼到RouteManager中實現(xiàn)的。關(guān)于Route的資料網(wǎng)上有不少,園子里也有不少朋友寫過,這里就不做說明了。
接就上面方法就會運行InitializeWindsor(),這就是Windsor容器初始化的方法:
ASP.NET MVC 示例代碼
- /// < summary>
- /// This web application uses the Castle Project's IoC container, Windsor see:
- /// http://www.castleproject.org/container/index.html
- /// < /summary>
- protected virtual void InitializeWindsor()
- {
- if (container == null)
- {
- // create a new Windsor Container
- container = ContainerBuilder.Build("Configuration\\Windsor.config");
- WcfConfiguration.ConfigureContainer(container);
- ServiceLocator.SetLocatorProvider(() => container.Resolve< IServiceLocator>());
- // set the controller factory to the Windsor controller factory (in MVC Contrib)
- System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
- }
- }
注:“Configuration\\Windsor.config”中的內(nèi)容較長,主要是一些XML配置節(jié)點。大家可以抽時間閱讀一下即可。
這個方法是今天講解的主要內(nèi)容,下面就介紹一下其中的代碼。
首先是判斷container(IWindsorContainer類型)是否為空,如果容器為空則創(chuàng)建并初始化該容器。也就是調(diào)用ContainerBuilder(Suteki.Shop\ContainerBuilder)類的Build方法來從外部的config文件中加載默認信息。我們這里就看一下Build方法的實現(xiàn):
ASP.NET MVC 示例代碼:
- public static IWindsorContainer Build(string configPath)
- {
- var container = new WindsorContainer(new XmlInterpreter(configPath));
- // register handler selectors
- container.Kernel.AddHandlerSelector(new UrlBasedComponentSelector(
- typeof(IBaseControllerService),
- typeof(IImageFileService),
- typeof(IConnectionStringProvider)
- ));
- // automatically register controllers
- container.Register(AllTypes
- .Of< Controller>()
- .FromAssembly(Assembly.GetExecutingAssembly())
- .Configure(c => c.LifeStyle.Transient.Named(c.Implementation.Name.ToLower())));
- container.Register(
- Component.For< IUnitOfWorkManager>().ImplementedBy< LinqToSqlUnitOfWorkManager>().LifeStyle.Transient,
- Component.For< IFormsAuthentication>().ImplementedBy< FormsAuthenticationWrapper>(),
- Component.For< IServiceLocator>().Instance(new WindsorServiceLocator(container)),
- Component.For< AuthenticateFilter>().LifeStyle.Transient,
- Component.For< UnitOfWorkFilter>().LifeStyle.Transient,
- Component.For< DataBinder>().LifeStyle.Transient,
- Component.For< LoadUsingFilter>().LifeStyle.Transient,
- Component.For< CurrentBasketBinder>().LifeStyle.Transient,
- Component.For< ProductBinder>().LifeStyle.Transient,
- Component.For< OrderBinder>().LifeStyle.Transient,
- Component.For< IOrderSearchService>().ImplementedBy< OrderSearchService>().LifeStyle.Transient,
- Component.For< IEmailBuilder>().ImplementedBy< EmailBuilder>().LifeStyle.Singleton
- );
- return container;
- }
首先是讀入指定配置文件的XML結(jié)點信息,將構(gòu)造一個 WindsorContainer實現(xiàn),同時在其微內(nèi)核中添加“容器處理組件”的方式(AddHandlerSelector),注意這種處理方式是按我們在業(yè)務(wù)邏輯中規(guī)定的方式處理的。
緊跟著又向該容器中注冊了Controller,而且配置屬性的LifeStyle被指定為Transient類型,這里有必要介紹一下Castle容器的組件生存周期,主要有如下幾種:
Singleton : 容器中只有一個實例將被創(chuàng)建
Transient : 每次請求創(chuàng)建一個新實例
PerThread: 每線程中只存在一個實例
PerWebRequest : 每次web請求創(chuàng)建一個新實例
Pooled :使用"池化"方式管理組件,可使用PooledWithSize方法設(shè)置池的相關(guān)屬性。
可以看到在本項目中,組件的生命周期基本上都被指定成為Transient類型,即當請求發(fā)生時創(chuàng)建,在處理結(jié)束后銷毀。
接著再看一下該方法的其余代碼,也就是對ModelBinder,F(xiàn)ilter,Service這類業(yè)務(wù)邏輯的組件注冊。同時我們看到有的組類在進行接口注冊的同時還被綁定了默認的實現(xiàn)類,其這種硬編碼的方法是是一種“可選”方式。
說完了Build方法之前,再回到Global.asax文件中的InitializeWindsor方法,看一下其余的代碼。我們看到這樣一行:
- WcfConfiguration.ConfigureContainer(container);
類WcfConfiguration的ConfigureContainer方法就是繼續(xù)向當前創(chuàng)建的容器中添加組件,而這次要加入的組件是Windows Live Writer的IMetaWeblog接口實現(xiàn)類,如下:
- public static class WcfConfiguration
- {
- public static void ConfigureContainer(IWindsorContainer container)
- {
- var returnFaults = new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true };
- container.AddFacility< WcfFacility>(f =>
- {
- f.Services.AspNetCompatibility = AspNetCompatibilityRequirementsMode.Required;
- f.DefaultBinding = new XmlRpcHttpBinding();
- })
- .Register(
- Component.For< IServiceBehavior>().Instance(returnFaults),
- Component.For< XmlRpcEndpointBehavior>(),
- Component.For< IMetaWeblog>().ImplementedBy< MetaWeblogWcf>().Named("metaWebLog").LifeStyle.Transient
- );
- }
- }
如前面所說的,擴張單元插件(Facilities)可以在不更改原有組件的基礎(chǔ)上注入你所需要的功能代碼,這里就使用了其AddFacility方法來添加擴展單元來注冊并管理我們的Windows Live Writer組件。
下面繼分析InitializeWindsor方法中的其余代碼,看完了ConfigureContainer方法,接著就是下面這一行代碼了:
- ServiceLocator.SetLocatorProvider(() => container.Resolve< IServiceLocator>());
剛看到這一行讓我感覺似曾相識,記得以前在看Oxite的Global.asax中也看過類似的這樣一行代碼。
- ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(container));
只不過那個項目中用的是 Unity而不是Castle Windsor。但實際的功能是一樣的。即完成對容器中服務(wù)地址的解析綁定。有了它,就可以通過Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase中所定義的方法如:DoGetInstance或DoGetAllInstances 來獲取相應(yīng)的服務(wù)組件(集合)的實例。
比如本項目中的DoGetInstance及DoGetAllInstances()實現(xiàn)代碼如下:
(ASP.NET MVC 示例代碼:Suteki.Common\Windsor\WindsorServiceLocator.cs):
- protected override object DoGetInstance(Type serviceType, string key)
- {
- if (key != null)
- return container.Resolve(key, serviceType);
- return container.Resolve(serviceType);
- }
- /// < summary>
- /// When implemented by inheriting classes, this method will do the actual work of
- /// resolving all the requested service instances.
- /// < /summary>
- /// < param name="serviceType">Type of service requested.< /param>
- /// < returns>
- /// Sequence of service instance objects.
- /// < /returns>
- protected override IEnumerable< object> DoGetAllInstances(Type serviceType)
- {
- return (object[])container.ResolveAll(serviceType);
- }
注,對該WindsorServiceLocator類的IOC綁定在ContainerBuilder.Build中,如下:
- container.Register(
- Component.For< IUnitOfWorkManager>().ImplementedBy< LinqToSqlUnitOfWorkManager>().LifeStyle.Transient,
- Component.For< IFormsAuthentication>().ImplementedBy< FormsAuthenticationWrapper>(),
- Component.For< IServiceLocator>().Instance(new WindsorServiceLocator(container)),
而InitializeWindsor方法中的***一行代碼如下:
- System.Web.Mvc.ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(container));
這里要說明的是WindsorControllerFactory這個類是在 MvcContrib項目中提供的,用于構(gòu)造一個Castle項目類型的Controller工廠。
【編輯推薦】