Autofac全面解析系列(版本:3.5) – [使用篇(推薦篇):1.類型注冊]


前言

Autofac

Autofac是一套高效的依賴注入框架。

Autofac官方網站:http://autofac.org/

Autofac在Github上的開源項目:https://github.com/autofac/Autofac

Autofac安裝:通過VS的Nuget可以很方便的獲取。

 

IoC/DI

關於IoC與DI的概念,網上有很多相關的博客,大家可以稍微了解一下,對比一下。

我個人的理解就是按照英文的中文翻譯來理解的:

IoC: Inversion of Control 控制反轉,將控制權進行反轉,將本由自身控制的對象初始化交由外部IoC容器進行初始化;

DI: Dependency Injection 依賴注入,將對象依賴的其他對象,通過注入的方式進行初始化。

 

簡單示例

我們先通過一個簡單的例子來看看Autofac的基本使用方式以及一些概念:

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Class_1>();    //將Class_1注冊到Autofac容器中

        IContainer container = builder.Build();
        Class_1 obj = container.Resolve<Class_1>(); //從autofac容器中獲取Class_1對象
        Console.WriteLine(obj.Id);

        Console.Write("Press any key to continue...");
        Console.ReadKey();
    }
}
class Class_1
{
    public Guid Id { get; set; }

    public Class_1()
    {
        Id = Guid.NewGuid();
    }
}

上面的代碼演示了最簡單注冊/獲取方式。我們先通過ContainerBuilder進行類型注冊,然后在后面可以通過IContainer進行獲取類型實例。這里我們也明白了一點,類型是需要先進行注冊,然后才能夠通過Autofac進行獲取(后面會談到一些特殊的配置,但是這也屬於另類的注冊方式)。

然后我們能知道在Autofac中,先通過ContainerBuilder進行類型注冊,然后通過ContainerBuilder的Build方法來獲取IContainer類型實例,后面則可以通過該IContainer實例來獲取注冊類型的實例對象。

 

Autofac類型注冊

類型/泛型注冊

在簡單實例中,我們已經看到了一種注冊方式,那就是泛型注冊:

builder.RegisterType<Class_1>();

這種注冊方式很方便也很簡單,比較常用,但是有一個缺點就是,注冊的類型必須在當前項目或被當前項目引用,因為使用泛型,必須類型明確。針對這點,還有一種通過Type對象進行注冊的方式:

//字符串為類型完全名稱
builder.RegisterType(Type.GetType("AutofacBlog.Class_1"));

使用這種方式進行類型注冊,被注冊的類型可以不是被直接引用,但是注意,類型所在的程序集必須被加載。

這種注冊方式,在有插件或類似需要動態加載程序集的情況下比較使用,通過掃描程序集,獲取一些滿足指定條件的類型,來進行注冊。

 

程序集批量注冊

類型注冊中提到了通過掃描程序集,來獲取部分類型進行注冊。Autofac對此提供了一個方便的方式,可以直接通過程序集來篩選類型注冊:

//獲取當前應用程序加載程序集(C/S應用中使用)
var assembly = Assembly.GetExecutingAssembly();
var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(assembly); //注冊所有程序集類定義的非靜態類型

上述代碼中,通過RegisterAssemblyTypes方法,將assembly中所有自定義的非靜態類型都注冊到Autofac中,后面可以使用IContainer對象獲取所有該程序集中自定義的類型對象。

這種方式達到了批量的效果,但是通常,我們並不需要把所有的類型都進行注冊,所以Autofac提供了幾種過濾方式:

builder.RegisterAssemblyTypes(assembly)
    .Where(type => type.Namespace.Equals("IocAutofac.Example")); //條件判斷

這種方式相信大家都比較熟悉,Where+lambda的方式來進行條件判斷,lambda參數為Type類型,也就是程序集中的type。

builder.RegisterAssemblyTypes(assembly)
    .Except<Program>();  //排除Program類型

這種方式用來排除指定類型的注冊,當排除的個例比較少時,會比較適用。Except還有另一種用法,但是用法比較復雜,在此不進行介紹。

builder.RegisterAssemblyTypes(assembly)
    .InNamespace("IocAutofac.Example");  //類型在IocAutofac.Example命名空間中

被注冊的類型需要在指定命名空間中。

builder.RegisterAssemblyTypes(assembly)
    .InNamespaceOf<Program>();  //類型在Program所在的命名空間中*/

這種方式與上面一種方式比較相似,也是用來判斷命名空間的,這種方式是根據類型來獲取命名空間。

 

通過這種方式,我們可以對程序集中的類型進行批量注冊,類型/泛型方式在被注冊類型較少的情況下還是不錯的,但當被注冊類型很多的時候,一個一個的手寫注冊會顯得很無力,這時候就是程序集批量注冊顯威的時候了。

 

Lambda注冊

上面講到的兩種方式都是通過類型進行直接注冊的,這種注冊方式,在獲取時,會直接通過構造函數new出對象(關於構造函數的優選選擇在后面的博文中將進行說明),不會做更多的操作。有時,我們希望能夠在獲取對象時能夠自動的做更多的事情時,我們可以通過Lambda注冊來解決:

builder.Register(cc =>
{
    var clas1 = new Class_1();
    while (clas1.Id.ToString().Contains("a"))
    {
        clas1.Id = Guid.NewGuid();
    }
    return clas1;
});

上述代碼,實際注冊了Class_1類型,因為最后返回的對象類型為Class_1。

Register方法接收了一個lambda表達式作為參數,在lambda表達式中,我們可以做很多事,包括一些屬性注入(后續說明)、方法注入(后續說明)、條件判斷等等。

我們在通過Autofac獲取Class_1對象時,實際會執行這樣的一個表達式。另外,lambda表達式的參數cc的類型是IComponentContext,這里我們可以簡單的當做IContainer進行使用。

 

實例注冊

var clas1 = new Class_1();
clas1.Id = Guid.Empty;

builder.RegisterInstance(clas1);

通過RegisterInstance進行實例注冊,進行實例注冊時,我們需要注意,實例注冊可以作為一種單例注冊的方式,也就是在后面通過Autofac獲取Class_1對象時,獲取到的是注冊時的那個對象。並且,如果一個在某處修改了該對象,其他地方再獲取時,獲取到的就是修改后的對象。

 

Module注冊

在日常開發中,可能不同開發會負責不同的模塊進行單獨開發。在開發過程中,不同模塊不同開發可能都有自己的類型需要注冊到autofac中,但是如果每個人在注冊時,都去修改一個指定地方的代碼,這在進行代碼合並時,是令人痛苦的。更好的方式是,每個開發不同的模塊都有自己指定的類型注冊區域,這樣,在代碼合並時,會減少很多代碼沖突。

對於這種方式,Autofac已經為我們提供了:

class Program
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();

        builder.RegisterModule<ModuleA>();
        //這兩種注冊方式達到的效果都一樣
        builder.RegisterModule(new ModuleB());

        IContainer container = builder.Build();
        Class_1 clas1 = container.Resolve<Class_1>();
        Class_2 clas2 = container.Resolve<Class_2>();
        Console.WriteLine(clas1.Id);
        Console.WriteLine(clas2.ToString());

        Console.Write("Press any key to continue...");
        Console.ReadKey();
    }
}


class ModuleA : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<Class_1>();
    }
}

class ModuleB : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<Class_2>();
    }
}
class Class_2
{

}

上述代碼中,有兩個繼承自Module類的類:ModuleA、ModuleB,這兩個類型重寫了父類的Load方法,並在load方法中,分別注冊了Class_1與Class_2類型。然后在主程序中,通過RegisterModule對Module進行注冊。

通過這種方式,不同的開發就可以各自創建一個類繼承自Module,然后重寫Load方法,在Load方法進行自己的類型注冊,最后再進行Module的統一注冊。

Module注意說明

實際上,RegisterModule需要的參數,並不是繼承自Module類的,而是實現了IModule接口的類,而Module也是實現了IModule接口的。也就是我們也可以寫一個實現了IModule接口的類型,然后在RegisterModule時傳入。但是一般我們直接去繼承Module就好了,這種方式比較簡單方便,實現IModule的方式更為復雜,當然,功能也更多,在此就不進行介紹了。

 

程序集Module注冊

Module注冊,為多人開發提供了一種方便的注冊方式,但是我們也可以發現,這種方式,還是會需要手動注冊Module,如果Module過多,Module注冊代碼也會顯得多而雜,當然,可以通過人工管理來控制Module的量。但是Autofac還提供了一種更方便的方式,並且,對於類似Orchard的模塊開發(子模塊與主模塊無引用關系,通過程序集加載方式來加載子模塊)或是插件開發,我們沒辦法通過Registerodule來注冊無直接引用關系的Module。

對於上述的情況,Autofac提供了很好的方式來解決:

var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyModules(assembly);

上述代碼會注冊assembly程序集中所有實現了IModule接口的類型(多層繼承也算),這樣,我們只需要將取出所有程序集,然后通過RegisterAssemblyModules進行一次性注冊,就可以自動注冊所有Module了。

RegisterAssemblyModule還可以指定一個泛型類型:

builder.RegisterAssemblyModules<ModuleA>(assembly);

這樣注冊,是指定只注冊assembly程序集中繼承自ModuleA的Module。

 

尾述

本篇博文主要講述Autofac中比較常用也比較好用的注冊方式,並沒有把所有的注冊方式都講述出來。

個人非常推薦使用Module,每個項目擁有自己的一個Module,這樣的一個Module都有固定的未知,便於查找該項目中的注冊及依賴關系。

通過本篇博文,可以了解到幾種常用的注冊方式,以及最簡單的對象獲取方式,初學者可以簡單的試試。

本篇博文講述的內容可能與大家平時使用autofac的方式不一樣,平時使用時還會使用As,但是我個人認為這個不屬於單純注冊的內容,所以將這部分放到后面的博文中。


免責聲明!

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



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