天天看點

NopCommerce是如何使用Autofac實作依賴注入的

IOC和DI

IOC中文名被稱作控制反轉(Inversion of Control),DI被稱為依賴注入(Dependency Injection),可參考Martin Fowler的這篇文章來了解這兩個概念:IoC容器和DependencyInjection模式。使用控制反轉模式開發項目流程是先建立接口,然後再實作類,或許有人不習慣這樣的開發方法,但在規模較大的軟體架構中,這種方法卻可以有效的降低類之間的互相依賴的情況,不但能增加架構的彈性,也能有效的降低軟體的複雜度。

如果不考慮控制反轉的情況,采用直接建立類,并直接在應用層調用該類,如此一來,應用層的對象就會與BLL(業務邏輯層)對象高度依賴,這樣的依賴會導緻這兩個類無法拆開,進而增加了這個類的維護難度,同時導緻了單元測試難以進行。為了解決耦合度問題,進而引入了控制反轉的概念。

Autofac介紹

 Autofac是一款IOC架構,比較于其他的IOC架構,如Spring.NET、Unity、Castle等,它更顯得輕量級,同時保證了高性能。它具有以下優點:

  1. 和C#語言聯系緊密,可以使用C#語言的很多特性,譬如Lambda表達式等;
  2. 較低的學習曲線,隻需了解IoC和DI的概念以及在何時需要使用它們即可;
  3. XML配置支援;
  4. 自動裝配;
  5. 與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抽象類

MVC內建的Autofac子產品将會為HTTP抽象類添加HTTP 請求的生命收起範圍内的注冊。包括依稀抽象類:

  • HttpContextBase
  • HttpRequestBase
  • HttpResponseBase
  • HttpServerUtilityBase
  • HttpSessionStateBase
  • HttpApplicationStateBase
  • HttpBrowserCapabilitiesBase
  • HttpCachePolicyBase
  • VirtualPathProvider

需要使用上面的抽象應該使用容器的 RegisterModule方法來添加 AutofacWebTypesModule

builder.RegisterModule(newAutofacWebTypesModule());
      

4. 注入View page

您可以通過在容器建立之前添加 ViewRegistrationSource 到容器中 使屬性注入來使MVC頁面可用。

builder.RegisterSource(newViewRegistrationSource());
      

您的viewpage必須繼承MVC類中用于建立,當使用Razor試圖引擎時将需要繼承WebViewPage類:

public abstract class CustomViewPage : WebViewPage
{
    public IDependencyDependency { get; set; }
}      

當使用的是webform的試圖引擎時,ViewPage,ViewMasterPage和ViewUserControl類都得到相應的支援。

public abstract class CustomViewPage : ViewPage
{
    public IDependencyDependency { get; set; }
}      

必須確定您實際的試圖頁面繼承了您自定義的基類。在Razor視圖引擎.cshtml中可以使用@inherits指令來實作:

@inherits Example.Views.Shared.CustomViewPage
      

使用webform時可以做如下設定

5. 對Filter Attribute進行屬性注入

為過濾器使用屬性注入必須 在容器建立之前 調用 RegisterFilterProvider方法,并将其傳到 AutofacDependencyResolver

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

來源:http://www.iyiruan.com/Blog/Detail?id=43

轉載于:https://www.cnblogs.com/aneasystone/archive/2012/08/27/2659176.html