簡體   English   中英

使用 Autofac 的應用程序中的多個范圍

[英]Multiple scopes in the application using Autofac

我有一個關於 Autofac 生命周期范圍的問題。 是否允許在應用程序的不同位置創建多個作用域來解析類型?

在官方文檔中有這樣的聲明:

注意:一般來說,服務定位在很大程度上被認為是一種反模式,也就是說,在任何地方手動創建范圍並通過你的代碼散布使用容器不一定是最好的方法 使用 Autofac 集成庫,您通常不必執行我們在上面的示例應用程序中所做的操作。 相反,事情是從應用程序中的中心“頂級”位置解決的,手動解決的情況很少見。 當然,你如何設計你的應用程序取決於你。

如果我有多個視圖模型並且我需要在每個視圖模型中解析某些類型怎么辦?

例子:

    public class ClassA
    {
        //some code here

    }

    public class ClassB
    {
        //some code here

    }

    public class ClassC
    {
        //some code here

    }

    public class ViewModelA
    {
        public ViewModelA()
        {

        }


        public void Method()
        {
            //some code here
            using (var scope = Container.BeginLifetimeScope())
            {
                var typeC = scope.Resolve<ClassC>();
                //some code here
            }
        }

    }

    public class ViewModelB
    {
        public ViewModelB()
        {

        }

        public void Method()
        {
            //some code here
            using (var scope = Container.BeginLifetimeScope())
            {
                var typeA = scope.Resolve<ClassA>();
                var typeB = scope.Resolve<ClassB>();
                //some code here
            }
        }
    }

假設所有類型都在容器中注冊 - 在應用程序中傳播這樣的范圍是一個好習慣嗎? 你怎么理解?

此致。

TL; 博士

我有一個關於 Autofac 生命周期范圍的問題。 是否允許在應用程序的不同位置創建多個作用域來解析類型?

是的,這是允許的,即使它認為不是最好的方式——但絕對不是被禁止的方式。

如果我有多個視圖模型並且我需要在每個視圖模型中解析某些類型怎么辦?

正如@Raymond 所建議的那樣,只需通過構造函數注入來解決它們。 您不需要像解決問題這樣簡單的事情的范圍。

現在,讓我們稍微談談這個。

首先,如何使用構造函數注入? 沒有比這更簡單的了。 讓我們稍微修改一下您的示例:

public class ClassA
{
    //some code here

}

public class ClassB
{
    //some code here

}

public class ClassC
{
    //some code here

}

public class ViewModelA
{
    private ClassC typeC;

    // this is a constructor injection - yes, it's that simple.
    public ViewModelA(ClassC _typeC)
    {
        typeC = _typeC;
    }


    public void Method()
    {
        typeC.doStuff();
    }

}

public class ViewModelB
{
    private ClassA typeA;
    private ClassB typeB;

    public ViewModelB(ClassA _typeA, ClassB _typeB)
    {
        typeA = _typeA;
        typeB = _typeB;
    }

    public void Method()
    {
        typeA.doStuff();
        typeB.doAnotherStuff();
    }
}

就是這樣。 您只需在構造函數中指定參數並將這些參數保存在類的私有字段中的某處 - 就可以了。 假設它們確實存在,只需使用這些對象。 (雖然它們可以是可選的,但我現在會跳過它)。 如果 autofac 無法解析某些依賴項,它將拋出異常 - 因此,如果您的類被創建,您可以絕對確定所有依賴項都已解析並在構造函數參數中提供給它。 (同樣,為了簡潔和清晰起見,我將跳過一些更復雜的場景)。

它給了我們什么? 好吧,現在類ViewModelAViewModelB通常對任何容器或 DI 一無所知。 他們只知道有人(閱讀:“容器”)會以某種方式從外部提供幾個指定類型(通常是接口)的參數。 類本身現在對誰將創建它的依賴項、在何處、何時以及如何創建它一無所知——這不再是它關心的問題。 這就是依賴注入的全部意義:消除管理依賴的問題。

接下來是 - 這張圖片中的范圍在哪里? 嗯……無處可去。 :) 這是理想的情況:服務完全不知道 DI 容器的存在。

但終生范圍到底是什么? 它是一種控制(驚喜!)從中解決的對象的生命周期的方法。 您通常不需要它,除非您正在處理一些邊緣情況。

我們來看一個極其簡化的例子。 假設您有一些工作需要按計划對外部資源執行某種請求。 然而,執行請求的方法實際上是通用的:它事先不知道它正在執行的請求的確切類型,因為它是由方法的類型參數定義的,只有在運行時才能知道。

另外,假設您有另一個要求:您不需要將 api 服務保留在內存中,而是僅為執行請求而創建它們,然后在工作完成后立即丟棄它們。

它可能如下所示:

abstract class SomeSpecialApi<TSomeTypeParam> { // just some common base type
    public abstract string doGetStuff();
}

class SomeSpecialApi1 : SomeSpecialApi<SomeTypeParam1> where SomeTypeParam1 : TSomeTypeParam {

    private HttpClient _client;

    public SomeSpecialApi(HttpClient client) {
        _client = client;
    }

    public override string doGetStuff() {
        return _client.get(someUrlWeDontCareAbout).Result;
    }

}

class SomeSpecialApi2 : SomeSpecialApi<SomeTypeParam2> where SomeTypeParam2 : TSomeTypeParam {

    private SomeFtpClient _client;

    public SomeSpecialApi(SomeFtpClient client) {
        _client = client;
    }

    public override string doGetStuff() {
        // it's just something very different from the previous class
        return _client.read(someOtherPathWeDontCareAboutEither).Result;
    }

}

class JobPerformer {

    private ILifeTimeScope _scope;

    public JobPerformer(ILifeTimeScope scope) {
        _scope = scope;
    }

    void PerformJob<T>() {
        while (true) {
            using (var childScope = scope.BeginLifeTimeScope()) {
                var api = childScope.Resolve<SomeSpecialApi<T>>();
                var result = api.doGetStuff();
                // do something with the result
            } // here the childScope and everything resolved from it will be destroyed
            // and that's the whole purpose of the lifetime scopes.
            Thread.Sleep(1000);
        }
    }

}

你看到它是如何工作的嗎? 根據方法的類型參數容器將解析不同的類來完成工作。 因此,與其創建所有依賴項然后手動銷毀它們,您只需創建自己的作用域,請求所需服務的實例,然后在工作完成后銷毀作用域。 范圍銷毀負責銷毀您甚至不知道(也不應該)知道的所有依賴項。 與手動創建所有依賴項相比,這絕對是一個改進。

然而,這里的缺點是現在 JobPerformer 類必須知道 ILifeTimeScope 這樣的東西。 所以它在某種意義上類似於 ServiceLocator 模式:我們至少必須知道向請求依賴項,換句話說:JobPerformer 現在知道了一些關於我們容器的信息。 這是我們理想中想要避免的,還有其他方法可以實現:工廠; 擁有的實例; 容器中服務注冊的不同生命周期策略; 不同種類的生命周期范圍(至少在 autofac 中); 等等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM