本次主要學習一下Autofac中實現類型註冊的幾種方式,這裏並不打算一開始就從基於接口開發的服務關聯切入,而是先從一個簡單的類型註冊來學起,雖然實際開發中可能不會這麼做,但是個人感覺從這裏學起理解能能更加深刻
Autofac使用流程
- 按照Ioc(控制反轉)的思想構建你的應用
- 添加Autofac引用
- 創建ContainerBuilder
- 註冊組件
- 創建容器,將其保存以備後續使用
- 應用程序運行階段
- 從容器中創建一個生命週期
- 在此生命週期作用域內解析組件實例
nuget添加Autofac引用
測試代碼
這裏提供一個很簡單的類作爲測試
class SqlDal { public void Add() { Console.WriteLine("向數據庫寫入一條數據"); } }
創建容器
所有的Ioc框架都是類似的,它們的目的都是將類的實例化和調用解耦,調用者不再直接創建被調用者的實例,而是交由容器創建,只是在實現上有各自不同的方式
var builder = new ContainerBuilder();
普通註冊
泛型註冊:RegisterType<T>()
註冊的類型必須在當前項目或被當前項目引用,因爲使用泛型,必須類型明確
//將SqlDal類註冊到容器中 builder.RegisterType<SqlDal>();
//通過Resolve()方法獲取註冊類型的實例,不推薦這種方式獲取,這裏的代碼只作爲測試 using (var container = builder.Build()) { var sqlDal = container.Resolve<SqlDal>(); sqlDal.Add(); }
通過Type對象進行註冊:RegisterType(Type)
被註冊的類型可以不是被直接引用,但類型所在的程序集必須被加載,這種註冊方式在有插件或類似需要動態加載程序集的情況下使用,通過掃描程序集,獲取一些滿足指定條件的類型,來進行註冊。
通過在項目中引用CSharp.Tests.Model實現
Assembly assembly = Assembly.Load("CSharp.Tests.Model"); var type = assembly.GetType("CSharp.Tests.Model.AutofacTestModel"); builder.RegisterType(type); using (var container = builder.Build()) { var model = container.Resolve<AutofacTestModel>(); Console.WriteLine(model.SayHello()); }
TODO:將dll拷貝到bin目錄下不用引用dll也可以實現註冊,但是如何將類型作爲Resolve<T>泛型類型參數暫時不知道
lambda表達式註冊
之前的方式都是通過類型進行直接註冊的,這種註冊方式,在獲取時,會直接通過構造函數new出對象,不會做更多的操作。有時我們希望能夠在獲取對象時能夠自動的做更多的事情時,我們可以通過lambda註冊來解決,在lambda表達式中可以做很多事,包括一些屬性注入、方法注入、條件判斷等等
class SqlDal { public string Str { get; set; } public void Add() { Console.WriteLine("向SqlServer數據庫寫入一條數據,Str={0}", Str); } }
builder.Register(type => { //通過lambda表達式註冊時添加屬性值 var sqlDal = new SqlDal(); sqlDal.Str = "Test"; return sqlDal; });
實例註冊
通過RegisterInstance進行實例註冊,進行實例註冊時,需要注意,實例註冊可以作爲一種單例註冊的方式,也就是在後面通過Autofac獲取SqlDal對象時,獲取到的是註冊時的那個對象。並且,如果一個在某處修改了該對象,其他地方再獲取時,獲取到的就是修改後的對象
builder.RegisterInstance(new SqlDal()); using (var container = builder.Build()) { var sqlDal = container.Resolve<SqlDal>(); sqlDal.Add(); }
泛型註冊
通過RegisterGeneric() 這個方法實現泛型註冊,在容器中可以創建出泛型的具體對象
builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>)).InstancePerLifetimeScope(); using (IContainer container = builder.Build()) { var ListString = container.Resolve<IList<string>>(); }
Module註冊
在日常開發中,可能不同開發會負責不同的模塊進行單獨開發。在開發過程中,不同模塊不同開發可能都有自己的類型需要註冊到autofac中,但是如果每個人在註冊時,都去修改一個指定地方的代碼,這在進行代碼合併時,是令人痛苦的。更好的方式是,每個開發不同的模塊都有自己指定的類型註冊區域,這樣,在代碼合併時,會減少很多代碼衝突
class SqlModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<SqlDal>(); } } class MySqlModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { builder.RegisterType<MySqlDal>(); } }
builder.RegisterModule<SqlModule>(); builder.RegisterModule<MySqlModule>(); using (var container = builder.Build()) { var sqldal = container.Resolve<SqlDal>(); sqldal.Add(); var mysqldal = container.Resolve<MySqlDal>(); mysqldal.Add(); }
上述代碼中,有兩個繼承自Module類的類:SqlModule、MySqlModule,這兩個類型重寫了父類的Load方法,並在load方法中,分別註冊了SqlDal與MySqlDal類型。然後在主程序中,通過RegisterModule對Module進行註冊。
通過這種方式,不同的開發就可以各自創建一個類繼承自Module,然後重寫Load方法,在Load方法進行自己的類型註冊,最後再進行Module的統一註冊(這裏還可以通過自定義實現了IModule接口的類型,然後在RegisterModule時傳入來達到同樣的效果並且功能也更多)
默認的註冊
如果一個類型被多次註冊,以最後註冊的爲準。通過使用PreserveExistingDefaults() 修飾符,可以指定某個註冊爲非默認值。
批量註冊、程序集註冊
上面的例子中都是將單個類型註冊到容器中,而在實際開發中可能存在多個類型需要註冊,難道要每個類型挨個註冊嗎?Autofac中爲這種情況提供了程序集註冊的方式
程序集批量註冊
類型註冊中提到了通過掃描程序集,來獲取部分類型進行註冊。Autofac對此提供了一個方便的方式,可以直接通過程序集來篩選類型註冊
//獲取當前應用程序加載程序集(C/S應用中使用)
var assembly = Assembly.GetExecutingAssembly();
//註冊所有程序集類定義的非靜態類型
builder.RegisterAssemblyTypes(assembly);
程序集過濾後批量註冊
上面的方式達到了批量的效果,但是通常並不需要把所有的類型都進行註冊,所以Autofac提供了幾種過濾方式
builder.RegisterAssemblyTypes(assembly).Where(type => type.Namespace.Equals("CSharp.Tests.框架學習"));
排除指定類型的註冊
使用Except排除指定類型的註冊
builder.RegisterAssemblyTypes(assembly).Where(type => type.Namespace.Contains("CSharp.Tests")).Except<CSharp.Tests.設計思想.SqlDal>();
程序集Module註冊
Module註冊,爲多人開發提供了一種方便的註冊方式,但是也可以發現,這種方式,還是會需要手動註冊Module,如果Module過多,Module註冊代碼也會顯得多而雜,當然,可以通過人工管理來控制Module的量。但是Autofac還提供了一種更方便的方式,並且,對於類似Orchard的模塊開發(子模塊與主模塊無引用關係,通過程序集加載方式來加載子模塊)或是插件開發,我們沒辦法通過Registerodule來註冊無直接引用關係的Module
var assembly = Assembly.GetExecutingAssembly();
//註冊assembly程序集中所有實現了IModule接口的類型(多層繼承也算),這樣只需要取出所有程序集,然後通過RegisterAssemblyModules進行一次性註冊,就可以自動註冊所有Module了
builder.RegisterAssemblyModules(assembly);
builder.RegisterAssemblyModules<SqlModule>(assembly);//指定泛型類型只註冊assembly程序集中繼承自SqlModule的Module
被註冊的類型需要在指定類的命名空間中
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly).InNamespaceOf<AutofacTest>();