Autofac 建構式選擇小地雷

Autofac在使用上有許多需要注意的小細節,這一次遇到建構式的小地雷,特別紀錄一下。讓下一次在踩到時,可以快速地排除。

當在類別中有使用到Autofac ILifetimeScope時需要特別注意建構式參數。一般來說,如果有需要在類別中使用ILifetimeScope,會在類別建構式中將ILifetimeScope注入

public class TestService : ITestService
{
    private ILifetimeScope _lifetimeScope;

    public TestService()
    {
        Console.WriteLine("Construct not have parameter");
    }

    public TestService(ILifetimeScope lifetimeScope)
    {
        this._lifetimeScope = lifetimeScope;
        Console.WriteLine("Construct have parameter ILifetimeScope");
    }
}

這個時候如果還有一個沒有參數的建構式時,就會很容易踩到Autofac在Resolve時,建構式選擇的問題。Autofac的建構式選擇,會以符合最多參數的建構式來創建物件,所以當在註冊類別時,沒有特別指定參數,

public static class AutofacHelper
{
    public static IContainer AutofacContainer;
    public static void Init()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<TestService>().As<ITestService>();

        AutofacContainer = builder.Build();
    }
}

當在Resolve TestServcie時

class Program
{
    static void Main(string[] args)
    {
        AutofacHelper.Init();
        var testService = AutofacHelper.AutofacContainer.Resolve<ITestService>();
        Console.ReadLine();
    }
}

使用的建構式,就會跟自己以為的不一樣,如上述的程式在註冊類別和Resolve時,皆沒有傳入參數。所以應該會使用沒有參數的建構式,但是來看執行結果

Autofac在Resolve時,卻使用了有參數的建構式,通常在實務第一時間發現都會接WTF!!!

哪是為什麼Autofac使用了有參數的建構式呢?問題就在程式中使用了ILifetimeScope,因為這一項參數只要在建構式上有使用,Autofac就會自動傳入。如此一來Autofac自然就使用了有參數的。

那如果希望能夠使用ILifetimeScope,又不想透過建構式傳入時,該怎麼處理呢?

public class TestService : ITestService
{
    public ILifetimeScope LifetimeScope;

    public TestService()
    {
        Console.WriteLine("Construct not have parameter");
    }
}
public static class AutofacHelper
{
    public static IContainer AutofacContainer;
    public static void Init()
    {
        var builder = new ContainerBuilder();

        builder.Register<TestService>(p =>
        {
            var testService = new TestService();
            testService._lifetimeScope = p.Resolve<ILifetimeScope>();
            return testService;
        }).As<ITestService>();
        AutofacContainer = builder.Build();
    }
}

只需要稍微修改一下將LifetimeScope宣告為Public。在註冊時,改使用Register(func),並且在傳入的func中將TestService產生出來,並且Resolve出ILifetimeScope塞到TestService中就可以了。當然,如果不想把LifetimeScope宣告為Public,也可以透過公開方法的方式設定,這樣就可以避免將LifetimeScope宣告為Public。

結論

Autofac在使用上,地雷還不少。所以特別紀錄一下自己踩到的地雷,尤其是一些小細節,之後如果再踩到可以快速地找到解決方法。

免責聲明:

"文章一定有好壞,文章內容有對有錯,使用前應詳閱公開說明書"