.NET手記-為ASP.NET MVC程序集成Autofac


MVC

 

Autofac總是會緊跟最新版本的ASP.NET MVC框架,所以文檔也會一直保持更新。一般來講,不同版本的框架集成Autofac的方法一般不變。

MVC集成需要引用 Autofac.Mvc5 NuGet 包.

MVC 集成庫提供對控制器(Controller)、模型綁定器(model binders)、行為篩選器(action filters)和視圖(views)的依賴注入. 它也添了對 每次請求生命周期(per-request lifetime)的支持.

 

快速開始 Quick Start

 

為了將Autofac集成進MVC項目,你需要引用Autofac.MVC集成庫,注冊你的控制器以及設定依賴解析器。你也可以啟用其他可選的特性。

 

protected void Application_Start()
{
  var builder = new ContainerBuilder();

  // Register your MVC controllers.
  builder.RegisterControllers(typeof(MvcApplication).Assembly);

  // OPTIONAL: Register model binders that require DI.
  builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
  builder.RegisterModelBinderProvider();

  // OPTIONAL: Register web abstractions like HttpContextBase.
  builder.RegisterModule<AutofacWebTypesModule>();

  // OPTIONAL: Enable property injection in view pages.
  builder.RegisterSource(new ViewRegistrationSource());

  // OPTIONAL: Enable property injection into action filters.
  builder.RegisterFilterProvider();

  // Set the dependency resolver to be Autofac.
  var container = builder.Build();
  DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

 

接下來的部分將說明關於這些特性進一步的細節以及如何使用它們。

 

注冊控制器 Register Controllers

 

在應用程序啟動時,你在構建自己的容器(ContainerBuilder)時, 也應該注冊所有的控制器以及它們的依賴項. 這通常會發生在典型的OWIN啟動類或者  Global.asax 的 Application_Start 方法中.

 

var builder = new ContainerBuilder();

// You can register controllers all at once using assembly scanning...
builder.RegisterControllers(typeof(MvcApplication).Assembly);

// ...or you can register individual controlllers manually.
builder.RegisterType<HomeController>().InstancePerRequest();

 

注意,ASP.NET MVC通過實體類型來請求控制器實例,所以不能將他們注冊為 As<IController>(). 同時,如果你手動注冊控制器且選擇定制生命周期,你必須將他們注冊為InstancePerDependency() 或者 InstancePerRequest()- 在這種情況下,如果你嘗試為多個請求重用一個控制器實例,ASP.NET MVC程序將會拋出異常

 

設定依賴解析器 Set the Dependency Resolver

 

在構建好容器后, 將他傳入一個AutofacDependencyResolver 類的實例中. 使用靜態方法 DependencyResolver.SetResolver 來使 ASP.NET MVC知道它應該使用 AutofacDependencyResolver 來定位服務(Service). 這是一個 IDependencyResolver 接口的Autofac實現.

 

var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

 

注冊模型綁定器 Register Model Binders

 

你可以使用的一個可選步驟是確保對模型綁定器的依賴注入。和注冊控制器類似,模型綁定器(IModelBinder接口的實現類)能夠在應用程序啟動時注冊進容器中。你可以使用 RegisterModelBinders() 方法來這樣做。你同時也必須記住要使用 RegisterModelBinderProvider() 拓展方法注冊 AutofacModelBinderProvider。這是一個 IModelBinderProvider 接口的Autofac實現。

 

builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
builder.RegisterModelBinderProvider();

 

由於 RegisterModelBinders() 方法采用集合(Assembly)掃描來添加模型綁定器,所以你需要指定模型綁定器為哪種類型注冊。

可以通過 Autofac.Integration.Mvc.ModelBinderTypeAttribute 屬性標簽來指定,如下:

 

[ModelBinderType(typeof(string))]
public class StringBinder : IModelBinder
{
  public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
  {
    // Implementation here
  }
}

 

如果要注冊為多個類型的模型綁定器,可以直接為類添加多個 ModelBinderTypeAttribute 標簽。

 

注冊網絡抽象 Register Web Abstractions

 

MVC集成庫包括了一個Autofac模塊,這個模塊能夠為網絡抽象類添加HTTP請求生命周期域的注冊 . 這允許你將網絡抽象作為你的類的依賴,同時能夠在運行時獲取注入的正確值(Value).

 

這包括了下面的抽象類:

 

  • HttpContextBase
  • HttpRequestBase
  • HttpResponseBase
  • HttpServerUtilityBase
  • HttpSessionStateBase
  • HttpApplicationStateBase
  • HttpBrowserCapabilitiesBase
  • HttpFileCollectionBase
  • RequestContext
  • HttpCachePolicyBase
  • VirtualPathProvider
  • UrlHelper

為了使用這些抽象類,通過使用標准的 RegisterModule() 方法來將 AutofacWebTypesModule 添加到容器中。

 

builder.RegisterModule<AutofacWebTypesModule>();

 

 

確保視圖頁面的屬性注入 Enable Property Injection for View Pages

 

 在構建應用容器之前,你可以通過將ViewRegistrationSource添加到你的ContainerBuilder使得可以在視圖頁中使用屬性注入。

 

builder.RegisterSource(new ViewRegistrationSource());

 

 你的視圖頁必須繼承一個MVC用於創建視圖的基類。在使用Razor視圖引擎時,這時的基類是指WebViewPage類。

 

public abstract class CustomViewPage : WebViewPage
{
  public IDependency Dependency { get; set; }
}

 

使用Web form引擎時,支持的基類為ViewPageViewMasterPage以及ViewUserControl

 

public abstract class CustomViewPage : ViewPage
{
  public IDependency Dependency { get; set; }
}

 

確保你實際的視圖頁繼承於你的自定義基類。對於Razor視圖引擎,你可以直接在.cshtml文件中使用@Inherits來實現.

 

@inherits Example.Views.Shared.CustomViewPage

 

使用Web Form引擎時,你可以在.aspx文件中的@Page里直接設置Inherits屬性來實現。

 

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="Example.Views.Shared.CustomViewPage"%>

 

由於ASP.NET MVC內部的問題,在Razor Layout頁面中是不能使用依賴注入的。Razor視圖中則可以使用,但是布局頁面是不可以的。

 

確保對行文篩選器的屬性注入 Enable Property Injection for Action Filters

 

為了能夠為你的篩選器屬性使用屬性注入,需要在構建容器並將容器提供為AutofacDependencyResolver之前調用ContainerBuilderRegisterFilterProvider()方法。

 

builder.RegisterFilterProvider();

 

這將允許你添加屬性到你的篩選器同時任何在容器中注冊的匹配依賴項都會被注入到這些屬性中。

例如,下面的行為篩選器有一個從容器中注入的ILogger實例(假設你注冊了一個ILogger組件)。注意,自身不必在容器中注冊。

 

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;
  }
}

 

在應用篩選器到你的action中,它會像正常一樣工作。

 

[CustomActionFilter]
[CustomAuthorizeAttribute]
public ActionResult Index()
{
}

 

 

OWIN集成 OWIN Integration

 

如果你正在使用MVC作為OWIN程序的一部分,那么你需要做下面的事情:

  • 做完所有標准的MVC集成步驟-注冊控制器,設置依賴解析器等等
  • 使用 Autofac 基本OWIN集成步驟設置你的應用。
  • 添加對 Autofac.Mvc5.Owin 包的引用.
  • 在你的應用啟動類中, 在注冊完基本Autofac中間件后再注冊Autofac MVC中間件.

 

 

public class Startup
{
  public void Configuration(IAppBuilder app)
  {
    var builder = new ContainerBuilder();

    // STANDARD MVC SETUP:

    // Register your MVC controllers.
    builder.RegisterControllers(typeof(MvcApplication).Assembly);

    // Run other optional steps, like registering model binders,
    // web abstractions, etc., then set the dependency resolver
    // to be Autofac.
    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    // OWIN MVC SETUP:

    // Register the Autofac middleware FIRST, then the Autofac MVC middleware.
    app.UseAutofacMiddleware(container);
    app.UseAutofacMvc();
  }
}

 

 

使用“插件”集合 Using “Plugin” Assemblies

 

 

如果你在一個插件集合中有沒有被主程序引用的控制器,那么你需要使用ASP.NET BuildManager注冊你的控制器插件集合.

你可以通過配置文件或者編程來實現這一步驟.

如果你選擇配置文件, 你需要添加你插件集合到/configuration/system.web/compilation/assemblies 列表. 如果你的插件集合沒有在 bin 文件夾, 你也需要去更新 /configuration/runtime/assemblyBinding/probing 路徑.

 

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <!--
          If you put your plugin in a folder that isn't bin, add it to the probing path
      -->
      <probing privatePath="bin;bin\plugins" />
    </assemblyBinding>
  </runtime>
  <system.web>
    <compilation>
      <assemblies>
        <add assembly="The.Name.Of.Your.Plugin.Assembly.Here" />
      </assemblies>
    </compilation>
  </system.web>
</configuration>

 

如果你選擇編程來注冊,你需要在應用預啟動中處理.

創建一個初始化類來實現集合掃描/載入同時使用BuildManager注冊它們:

 

using System.IO;
using System.Reflection;
using System.Web.Compilation;

namespace MyNamespace
{
  public static class Initializer
  {
    public static void Initialize()
    {
      var pluginFolder = new DirectoryInfo(HostingEnvironment.MapPath("~/plugins"));
      var pluginAssemblies = pluginFolder.GetFiles("*.dll", SearchOption.AllDirectories);
      foreach (var pluginAssemblyFile in pluginAssemblyFiles)
      {
        var asm = Assembly.LoadFrom(pluginAssemblyFile.FullName);
        BuildManager.AddReferencedAssembly(asm);
      }
    }
  }
}

 

然后使用集合屬性注冊你的預啟動代碼:

 

[assembly: PreApplicationStartMethod(typeof(Initializer), "Initialize")]

 

使用當前的Autofac依賴解析器 Using the Current Autofac DependencyResolver

 一旦你將 MVC DependencyResolver 設置為 AutofacDependencyResolver, 你就可以使用 AutofacDependencyResolver.Current 作為獲取當前依賴解析器的快捷方式 同時將它作為 AutofacDependencyResolver 使用.

不幸的是,使用AutofacDependencyResolver.Current 存在很多陷阱,它們可能導致一些東西不會正常工作. 通常這些問題是由類似 Glimpse 或者Castle DynamicProxy 的產品導致的。為了添加功能,它們可能會折疊(封裝?)或修改依賴解析器.如果當前的依賴解析器被修改或者代理, 你不能將它當作AutofacDependencyResolver使用同時也沒有方法獲得真實的解析器.

在Autofac MVC集成庫版本3.3.3之前, 我們通過動態地添加依賴解析器到請求生命周期域來追蹤它. 這導致我們可以繞開不能從代理中解綁 AutofacDependencyResolver的問題 ... 但是這樣意味着AutofacDependencyResolver.Current 僅僅在請求生命周期域中起作用 - 你不能在后台任務或者應用啟動時使用它.

版本 3.3.3開始的時候, 定位 AutofacDependencyResolver.Current 的邏輯改為首先計算當前的依賴解析器; 然后特地尋找使用 Castle DynamicProxy 被折疊的簽名同時通過反射來解除折疊. 但是未能... 我們無法找到AutofacDependencyResolver 所以我們拋出了一個如下異常:

The dependency resolver is of type ‘Some.Other.DependencyResolver’ but was expected to be of type ‘Autofac.Integration.Mvc.AutofacDependencyResolver’. It also does not appear to be wrapped using DynamicProxy from the Castle Project. This issue could be the result of a change in the DynamicProxy implementation or the use of a different proxy library to wrap the dependency resolver.

最典型的地方是當我們通過 ContainerBuilder.RegisterFilterProvider() 使用行為篩選提供器(action filter provider). 篩選器提供者需要訪問依賴解析器 同時能夠使用 AutofacDependencyResolver.Current 來實現.

如果你看到這些,這意味着你正在用某種不能被折疊的方式修改(或者直譯為裝飾?)解析器,依賴於 AutofacDependencyResolver.Current 的功能將會失敗. 當前的解決方案不會修改/裝飾?解析器.

 

單元測試 Unit Testing

 

當對一個使用了 InstancePerRequest 組件的ASP.NET MVC 應用進行單元測試時, 你在嘗試解析它的實例時將會拋出一個異常.這是因為在單元測試時沒有HTTP請求。

每次請求生命周期域(per-request lifetime scope )這篇主題文章概述了對 per-request-scope 組件測試的策略.

 

 

示例實現 Example Implementation

 

Autofac源碼中包含了一個示例web應用程序,叫做Remember.Web。它演示了各種使用Autofac對MVC的注入方式。

項目地址:https://github.com/autofac/Autofac

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM