IOC和DI
IOC中文名被稱作控制反轉(Inversion of Control),DI被稱為依賴注入(Dependency Injection),可參考Martin Fowler的這篇文章來了解這兩個概念:IoC容器和DependencyInjection模式。使用控制反轉模式開發項目流程是先建立接口,然后再實現類,或許有人不習慣這樣的開發方法,但在規模較大的軟件架構中,這種方法卻可以有效的降低類之間的互相依賴的情況,不但能增加架構的彈性,也能有效的降低軟件的復雜度。
如果不考慮控制反轉的情況,采用直接創建類,並直接在應用層調用該類,如此一來,應用層的對象就會與BLL(業務邏輯層)對象高度依賴,這樣的依賴會導致這兩個類無法拆開,從而增加了這個類的維護難度,同時導致了單元測試難以進行。為了解決耦合度問題,從而引入了控制反轉的概念。
Autofac介紹
Autofac是一款IOC框架,比較於其他的IOC框架,如Spring.NET、Unity、Castle等,它更顯得輕量級,同時保證了高性能。它具有以下優點:
- 和C#語言聯系緊密,可以使用C#語言的很多特性,譬如Lambda表達式等;
- 較低的學習曲線,只需了解IoC和DI的概念以及在何時需要使用它們即可;
- XML配置支持;
- 自動裝配;
- 與ASP.NET MVC3集成;(Orchard也是使用Autofac實現IOC的)
在MVC3項目中使用Autofac
在MVC3工程中使用Autofac的最好也是最簡單的方法是使用NuGet來安裝Autofac.Mvc3,安裝完成以后,在Global.asax的Application_Start方法中添加如下代碼:
protected void Application_Start() { var builder = new ContainerBuilder(); builder.RegisterControllers(typeof(MvcApplication).Assembly); var container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); // Other MVC setup...
這樣就開啟了Controller的依賴注入功能。其中的DependencyResolver是一個全局靜態類,MVC3提供了對依賴注入的支持,SetResolver函數用於設置使用哪個Resolver(解析器)來進行依賴注入,這里使用的是Autofac的依賴注入解析器。如果要使用自己的解析器,必須在這里使用SetResolver函數設置。
1. 注冊Controller
可以使用下面的方法對特定的Controller進行注冊:
var builder = new ContainerBuilder();
builder.RegisterType<HomeController>().InstancePerRequest();
同時可以使用Autofac提供的RegisterControllers擴展方法來對程序集中所有的Controller一次性的完成注冊:
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
2. 注冊Model Binder
與控制器的注冊類似,模型綁定也可以再Global.asax.cs中注冊。您可以通過如下操作完成整個程序集的注冊:
var builder = newContainerBuilder(); builder.RegisterModelBinders(Assembly.GetExecutingAssembly()); builder.RegisterModelBinderProvider();
您也必須記住使用RegisterModelBinderProvider擴展方法來注冊RegisterModelBinderProvider。這個方法用是Autofac對IModelBinderProvider接口的實現。
因為RegisterModelBinders擴展方法通過掃描程序集來添加模型綁定的,所以您需要指定IModelBuilder注冊的目標類是什么類型。
[ModelBinderType(typeof(string))] public class StringBinder : IModelBinder { public override object BindModel(ControllerContext controllerContext,ModelBindingContext bindingContext) { //do implementation here } }
多行的ModelBuilderTypeAttribute實例可以添加到需要對個類型注冊的類中。
3. 注入HTTP抽象類
- HttpContextBase
- HttpRequestBase
- HttpResponseBase
- HttpServerUtilityBase
- HttpSessionStateBase
- HttpApplicationStateBase
- HttpBrowserCapabilitiesBase
- HttpCachePolicyBase
- VirtualPathProvider
builder.RegisterModule(newAutofacWebTypesModule());
4. 注入View page
builder.RegisterSource(newViewRegistrationSource());
public abstract class CustomViewPage : WebViewPage { public IDependencyDependency { get; set; } }
public abstract class CustomViewPage : ViewPage { public IDependencyDependency { get; set; } }
@inherits Example.Views.Shared.CustomViewPage
<%@ PageLanguage="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Example.Views.Shared.CustomViewPage" %>
5. 對Filter Attribute進行屬性注入
ContainerBuilder builder = new ContainerBuilder(); builder.RegisterControllers(Assembly.GetExecutingAssembly()); builder.Register(c => new Logger()).As<ILogger>().InstancePerHttpRequest(); builder.RegisterFilterProvider(); IContainer container = builder.Build(); DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
public class CustomActionFilter : ActionFilterAttribute { public ILogger Logger { get; set; } public override void OnActionExecuting(ActionExecutingContext filterContext) { Logger.Log("OnActionExecuting"); } }
public class CustomAuthorizeAttribute : AuthorizeAttribute { public ILogger Logger { get; set; } protected override bool AuthorizeCore(HttpContextBase httpContext) { Logger.Log("AuthorizeCore"); return true; } }
[CustomActionFilter] [CustomAuthorizeAttribute] public ActionResult Index() { // ... }
關於Autofac更多的信息,可以參考autofac在google code上的wiki文檔:http://code.google.com/p/autofac/wiki/Mvc3Integration
NopCommerce是如何使用Autofac實現依賴注入的?
NopCommerce將所有和Autofac注入相關的工作都放到了EngineContext中,在Global.asax的Application_Start函數的第一句代碼即是:
//initialize engine context EngineContext.Initialize(false);
從這里開始EngineContext的初始化工作,初始化時會創建一個新的NopEngine,參數false指定當NopEngine不為空時是否重新生成一個新的NopEngine。
[MethodImpl(MethodImplOptions.Synchronized)] public static IEngine Initialize(bool forceRecreate) { if (Singleton<IEngine>.Instance == null || forceRecreate) { var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; Debug.WriteLine("Constructing engine " + DateTime.Now); Singleton<IEngine>.Instance = CreateEngineInstance(config); Debug.WriteLine("Initializing engine " + DateTime.Now); Singleton<IEngine>.Instance.Initialize(config); } return Singleton<IEngine>.Instance; }
NopEngine使用單例模式,在整個程序運行期間存在一個實例,代碼首先會判斷NopEngine是否為空,為空的話則根據web.config中配置的NopConfig節點信息創建一個新的NopEngine實例,然后對該實例進行初始化操作。web.config中的配置信息如下:
<configSections> <section name="NopConfig" type="Easy.Core.Configuration.NopConfig, Easy.Core" requirePermission="false" /> </configSections> <NopConfig> <DynamicDiscovery Enabled="true" /> <Engine Type="" /> <Themes basePath="~/Themes/" /> </NopConfig>
CreateEngineInstance函數中使用new NopEngine()創建了一個NopEngine實例,在NopEngine的構造函數處對Autofac的容器(Container)作了初始化,如下代碼:
public NopEngine(EventBroker broker, ContainerConfigurer configurer) { var config = ConfigurationManager.GetSection("NopConfig") as NopConfig; InitializeContainer(configurer, broker, config); }
private void InitializeContainer(ContainerConfigurer configurer, EventBroker broker, NopConfig config) { var builder = new ContainerBuilder(); _containerManager = new ContainerManager(builder.Build()); configurer.Configure(this, _containerManager, broker, config); }
NopCommerce通過ContainerManager對容器做了一層封裝,方便對其他類型的IOC框架的擴充和支持。Configure函數完成了所有依賴的注入,同時查找所有實現了IDependencyRegistrar接口的類,並調用其Register方法,注冊內容包括Http context、web helper、controller、data layer、plugin、cache manager、work context、services、settings、event consumers等等。
關於ContainerManager/ContainerConfigurer和IDependencyRegistrar是實現IOC的關鍵,下面對這兩個部分做詳細的討論。
// todo:仍需繼續分析具體實現
ContainerManager/ContainerConfigurer
ContainerManagerContainerManager對依賴注入中使用的容器做了一層封裝,提供了這些函數:
- AddComponent/AddComponentInstance/AddComponentWithParameters
- Resolve/ResolveAll/ResovleUnregistered
- UpdateContainer
DependencyRegistrar
- web helper
- controller
- data layer
- plugin
- cache manager
- work context
- services
- settings
- event consumers