【寫在前面】嘗試做完一件工作之外自我覺得有意義的一件事,那就從翻譯Autofac的幫助文檔吧。
入門指南
將Autofac集成你的應用程序的步驟通常很簡單,一般是:
- 時刻以IOC(控制反轉)的思想來規划你的應用程序
- 在你的Porject中添加Autofac引用
- 按照如下步驟設計應用程序的啟動環節
- 創建一個ContainerBuilder
- 向ContainerBuilder注冊組件
- 通過ContainerBuilder的Build()方法獲得Container(后續需用到)
- 在應用程序運行環節時,按如下步驟執行
- 通過Container獲得一個lifetime scope
- 通過lifetime scope解析出組件實例
- 通過實例繼續執行
本篇將通過入門指南實現一個簡單的Console應用程序,一旦有了基礎,后續可以延伸集成WCF、ASP.NET等等。
規划應用程序
先簡單解釋一下IOC(控制反轉)。
通常情況下,類A若需要類B的一些功能,則在類A中需要通過“new”操作來完成創建和使用功能,這樣在代碼層面就固定住了類A和類B之間的相互依賴關系。這樣的“依賴”,在后續的重構或修改時,影響面很大。
而IOC(控制反轉)的思維,則是取消,由類A來創建類B,變更為在執行期間,由IOC容器來根據需要和約定自動創建類B來給類A使用。
Martin Fowler有一篇著名的文章,解釋何謂IOC,可以點擊“鏈接”查看。
在本篇的示例中,我們將定義一個類,這個類可以輸出一些數據;但我們不想和Console綁定住,因為我們並不能確定在實際使用過程中Console是否真的可用。
我們通常可以這樣實現:
using System; namespace DemoApp { // 定義個輸出接口 public interface IOutput { void Write(string content); } // 為接口實現Console方式的輸出 public class ConsoleOutput : IOutput { public void Write(string content) { Console.WriteLine(content); } } // 定義一個書寫器接口 public interface IDateWriter { void WriteDate(); } // 實現書寫器接口,輸出當前日期,請注意,它依賴於IOutput接口 // 在運行時,才能獲得具體的輸出形式 public class TodayWriter : IDateWriter { private IOutput _output; public TodayWriter(IOutput output) { this._output = output; } public void WriteDate() { this._output.Write(DateTime.Today.ToShortDateString()); } } }
至此,我們已經有了IOC思維了。下一步,我們將通過Autofac來實現它。
添加Autofac引用
第一步,需要在項目中添加Autofac的引用。建議通過NuGet方式(如下圖),將會自動添加到項目中。
Autofac,除了Core之外,還有很多擴展,例如和ASP.NET MVC集成等等。
截至目前為止,Autofac Core最新版本為4.6.0,對應.net framework 4.5.
應用程序啟動
在應用程序啟動環節,你需要創建一個ContainerBuilder來注冊組件。
一個組件,可以是一個表達式,一個.NET類型,又或者可以是一個或者多個帶依賴項的Service等等。
簡單來說,如下這個實現接口的.NET類,我們有兩種方式來定義它的類型。
public class SomeType : IService { }
- 作為類型自身,那就是SomeType
- 作為接口,那就是IService
如上的例子,組件就是SomeType,同時也是包括SomeType和IService的兩種形態的服務。
在Autofac,你可以按照如下方式向ContainerBuilder注冊組件。
// 創建builder. var builder = new ContainerBuilder(); // 通常情況下,我們感興趣的是IService: builder.RegisterType<SomeType>().As<IService>(); // 或者,也可以同時注冊IService和SomeType builder.RegisterType<SomeType>().AsSelf().As<IService>();
對於我們的示例應用程序,我們需要注冊所有組件(類),並公開它們的服務(接口),這樣就可以很好地連接起來。
我們還需要保存Container,以便以后可以使用它來解析類型。
using System; using Autofac; namespace DemoApp { public class Program { private static IContainer Container { get; set; } static void Main(string[] args) { var builder = new ContainerBuilder(); builder.RegisterType<ConsoleOutput>().As<IOutput>(); builder.RegisterType<TodayWriter>().As<IDateWriter>(); Container = builder.Build(); // WriteDate在執行時,Autofac將自動根據注冊的組件進行裝配並執行. WriteDate(); } } }
現在我們有一個Container,所有的組件都已注冊,並且它們正在公開適當的服務。讓我們利用它吧。
應用程序執行
在應用程序執行,你需要使用你注冊的組件時,是通過lifetime scope來解析出相應組件實例的。
Container本身也是一個lifetime scope,從技術角度來說,你可以從Container直接解析出組件,然而並不推薦這樣用。
解析一個組件時,將會根據所定義的實例范圍,創建一個對象的新實例。(解析一個組件大致相當於調用“new”實例化一個類。很簡單的一個類比。),某些組件可能有特殊的Dispose(如組件本身實現了IDisposable)- Autofac可以在lifetime scope終止時也會自動處理這些組件特殊的Dispose。
Container這個lifetime scope,在應用程序執行時會一直存在。所以,如果用Container來解析所有的組件的話,可以想象當應用程序終止時,Dispose將是多么繁忙的景象(所有的組件等待被Dispose),同樣,因為直到最后一刻才會被Dispose,會更大幾率引發“內存不足”這個問題。
所以,推薦從Container創建一個新的lifetime scope來解析組件,在完成相關執行代碼后,lifetime scope連帶着組件即可被釋放。
(一旦你使用了Autofac integration libraries,創建和使用lifetime scope后就可以完全忘記它)
對於我們的示例應用程序,我們將創建一個lifetime scope,然后解析出writer這個組件,並執行“writedate”方法。
namespace DemoApp { public class Program { private static IContainer Container { get; set; } static void Main(string[] args) { // ...... } public static void WriteDate() { // 創建scope, 解析IDateWriter, // 使用它, 銷毀scope. using (var scope = Container.BeginLifetimeScope()) { var writer = scope.Resolve<IDateWriter>(); writer.WriteDate(); } } } }
現在運行程序…
- “writedate”方法要求一個idatewriter。
- autofac看到idatewriter映射到TodayWriter,開始創建一個todaywriter。
- autofac看到todaywriter需要ioutput來完成構造。
- autofac看到ioutput映射到consoleoutput,所以創建一個新的consoleoutput實例。
- autofac采用新consoleoutput實例完成建設TodayWriter。
- autofac返回完全構成TodayWriter,可以開始執行“writedate”了。
以后,如果你希望應用程序寫另一個日期的話,可以實現一個不同的idatewriter然后改變登記程序的啟動。你不需要改變其他的類。是的,這就是IOC(控制反轉)!
注意:一般來說,Service很大程度上被認為是一種反模式(看文章)。因此,在你的程序中人工創建scope並使用Container,這種方式不是最好的。使用“Autofac integration libraries”,你通常不需要像示例程序那樣人工創建scope。當然,你如何設計應用程序取決於你自己。
本篇結束。
后述:完整的英文版鏈接在此:http://autofac.readthedocs.io/en/latest/getting-started/index.html,有部分內容沒有翻譯,也有部分內容自我感覺翻譯的並不恰當。若你有意見和建議,可聯系我。
本博文屬作者原創,首發於www.boxfun.net,除非特別聲明,本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。