Autofac之類型關聯

前面的學習一直使用的是直接註冊類型並不是Autofac已經依賴注入的主要使用方式,最佳的依賴注入與Autofac的使用方式,都是要結合面向接口(抽象)編程的概念的。推崇的是依賴於抽象而不是具體

public interface IPerson
{
    void Say();
}

public class Worker : IPerson
{
    public void Say()
    {
        Console.WriteLine("工人!");
    }
}

public class Student : IPerson
{
    public void Say()
    {
        Console.WriteLine("學生!");
    }
}

public class AutoFacManager
{
    IPerson person;

    public AutoFacManager(IPerson MyPerson)
    {
        person = MyPerson;
    }

    public void Say()
    {
        person.Say();
    }
}

使用AS進行關聯

//IPerson類型的服務和Worker的組件連接起來,這個服務可以創建Worker類的實例
builder.RegisterType<Worker>().As<IPerson>();
using (var container = builder.Build())
{
    var obj = container.Resolve<IPerson>();
    obj.Say();
}

多關聯

public interface IPerson
{
    void Say();
}
public interface IDuty
{
    void Write();
}

public class Worker : IPerson, IDuty
{
    public void Say()
    {
        Console.WriteLine("工人!");
    }

    public void Write()
    {
        Console.WriteLine("我的工作內容是搬磚!");
    }
}

一個類可能實現多個接口,如果我們希望Resolve多個接口時獲取到的都是那個類型,應該怎麼做呢?最容易想到的方式就是直接再註冊As一遍就好了。Autofac提供了類似IEnumerable和IQueryable鏈式編程的方式,如果希望多個接口或類型都與同一個類型進行關聯,我們可以直接在表達式後面繼續As

builder.RegisterType<Worker>()
    .As<IPerson>()
    .As<IDuty>();
using (var container = builder.Build())
{
    var obj1 = container.Resolve<IPerson>();
    var obj2 = container.Resolve<IDuty>();
    obj1.Say();
    obj2.Write();
} 

自關聯AsSelf

 不使用As時,RegisterType註冊的是什麼類型,Resolve就使用什麼類型進行獲取,但是在使用As後,就只能使用As的類型進行Resolve;但是如果還想在Resolve<Worker>時能夠獲取到Worker而不拋出異常,應該怎麼辦呢?在不知道AsSelf方法的情況下,可以在註冊時直接As<Worker>,這樣就可以了。另一種方式就是AsSelf

builder.RegisterType<Worker>().As<IPerson>();//這麼寫獲取實例必須使用As類型進行Resolve 
builder.RegisterType<Worker>().As<IPerson>().AsSelf();//這麼寫可以使用原類型進行Resolve 
using (var container = builder.Build())
{
    var obj1 = container.Resolve<Worker>();//builder.RegisterType<Worker>().As<IPerson>().As<Worker>();或者builder.RegisterType<Worker>().As<IPerson>().AsSelf();都可以
    var obj2 = container.Resolve<IPerson>();//必須builder.RegisterType<Worker>().As<IPerson>();          
    obj1.Say();
    obj2.Say();
}

批量關聯AsImplementedInerfaces

之前說到可以通過多個As爲一個類型關聯多個接口,但是如果實現接口過多代碼還是不夠簡潔,通過AsImplementInterfaces方法,可以讓所有註冊類型自動與實現的接口進行關聯。當然,也正因爲這點,在使用AsImplementInterfaces時需要注意,是否真的希望與所有接口都進行關聯

builder.RegisterType<Worker>().AsImplementedInterfaces();
using (var container = builder.Build())
{
    var obj1 = container.Resolve<IPerson>();
    var obj2 = container.Resolve<IDuty>();
    obj1.Say();
    obj2.Write();
}

類型關聯注意點

一個接口/類型只能與一個類型進行關聯,前面我們看到了,可以將一個類型與多個接口進行關聯,讓多個接口Resolve結果都是同一個類型實例。需要注意的是,這個是不可逆的,也就是一個接口不能與多個類型進行關聯,因爲這樣autofac不知道應該返回哪個類型實例

builder.RegisterType<Worker>().As<IPerson>();
builder.RegisterType<Student>().As<IPerson>();
using (var container = builder.Build())
{
    var obj = container.Resolve<IPerson>();
    obj.Say();
}

最後實際與I接口關聯的類型是Student,Autofac中按註冊順序,後面註冊的會覆蓋前面註冊的。如果想要阻止這種默認行爲(相同接口/類型進行多次類型關聯,後面的關聯覆蓋前面的關聯),可以在As方法調用用繼續調用PreserveExistingDefaults方法,這樣,如果之前該接口/類型已經進行關聯註冊,則此次關聯無效

關聯類型與註冊類型需要時繼承/實現關係或爲其本身

builder.RegisterType<X>().As<Y>();

當這樣進行註冊關聯時,需要X繼承或實現Y,再或者Y就是X類型,否則將在builder調用Build方法時拋出異常。

1、類型

類型是描述服務的基本方法

builder.RegisterType<Worker>().As<IPerson>();  //IPerson類型的服務和Worker的組件連接起來,這個服務可以創建Worker類的實例

2、名字

服務可以進一步按名字識別。使用這種方式時,用 Named()註冊方法代替As()以指定名字

builder.RegisterType<Worker>().Named<IPerson>("worker");
builder.RegisterType<Student>().Named<IPerson>("student");
using (var container = builder.Build())
{
    var obj1 = container.ResolveNamed<IPerson>("worker");
    obj1.Say();
    var obj2 = container.ResolveNamed<IPerson>("student");
    obj2.Say();
}

3、鍵

有Name的方式很方便,但是值支持字符串,但有時候我們可能需要通過其他類型作鍵。

例如,使用枚舉作爲key:

public enum State { Worker, Student }
builder.RegisterType<Worker>().Keyed<IPerson>(State.Worker);
builder.RegisterType<Student>().Keyed<IPerson>(State.Student);
using (var container = builder.Build())
{
    var obj1 = container.ResolveKeyed<IPerson>(State.Worker);
    obj1.Say();
    var obj2 = container.ResolveKeyed<IPerson>(State.Student);
    obj2.Say();
}

ResolveKeyd()會導致容器被當做 Service Locator使用,這是不被推薦的。應該使用IIndex type替代。

IIndex索引,需要using Autofac.Features.Indexed

Autofac.Features.Indexed.IIndex<K,V>是Autofac自動實現的一個關聯類型。component可以使用IIndex<K,V>作爲參數的構造函數從基於鍵的服務中選擇需要的實現

builder.RegisterType<Student>().Keyed<IPerson>(State.Student);
using (IContainer container = builder.Build())
{
    IIndex<State, IPerson> IIndex = container.Resolve<IIndex<State, IPerson>>();
    IPerson p = IIndex[State.Student];
    p.Say();
}

IIndex中第一個泛型參數要跟註冊時一致,在例子中是State枚舉。其他兩種註冊方法沒有這樣的索引查找功能,這也是爲什麼設計者推薦Keyed註冊的原因之一

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章