簡體   English   中英

具有多個項目和抽象工廠的控制台應用程序中的簡單注入器

[英]Simple Injector in Console Application with multiple projects and an abstract factory

TL;博士。 我有一個循環依賴,不知道如何打破它。

Main.csproj:具有手動實例化 DiService 的 Program.cs

var diService = new DiService(new Container());
diService.Register();

register 方法在 CurrentDomain 中搜索程序集並注冊集合,其中給定接口存在多個實現,或者在 1-1 的基礎上注冊具體化。

然后它使用 Container 來實例化一個抽象工廠。

var diFactory = diService.Registry.GetInstance<IDiFactory>();

這里是工廠

public class DiFactory : IDiFactory
{
    private readonly Container registry;

    public DiFactory(Container registry)
    {
        this.registry = registry;
    }

    public T Get<T>()
    {
        var reqT = typeof(T);
        return (T) registry.GetInstance(reqT);
    }
}

解決方案中的項目依賴如下所示:

Main -> A -> B,E 
        B -> C,D,E
        C -> D,E
        D -> E

DiService 和 DiFactory 與其他服務位於項目 B 中。 並不是說這很重要。 如果他們在 Main,我想我會遇到同樣的問題。

項目 B 到 E 中的所有對象都有一個注入了 DiFactory 的構造函數,因此它們可以決定在運行時需要哪些對象。 但是要讓 C 使用它,它必須依賴於 B,這是一個循環依賴。

如果我將 DI 的東西移動到一個新項目 F,那么所有項目都可以依賴它,但是工廠如何在不創建另一個循環依賴的情況下引用其他項目中的類型?

我遵循了 IRequestHandler 的文檔,我只是沒有做字典。 很可能我有一個設計缺陷,但我看不到它是什么。

這是 LinqPad 對象之間交互的示例 - 無法編譯但看起來正確。

void Main()
{
    var diService = new Mine.Services.MyDiService();
    var diFactory = diService.Container.GetInstance<Mine.Services.IMyFactory>();
    var rand = new Random();
    var next = rand.Next(1, 100);
    var task = next % 2 == 0
        ? diFactory.Get<Mine.Tasks.EvenTask>()
        : (Mine.Tasks.IMyTask)diFactory.Get<Mine.Tasks.OddTask>();
    task.Perform();
}


namespace Mine.Common
{
    public class MyCommonObject { }
}

namespace Mine.Services
{
    public class FakeContainer
    {
        public T GetInstance<T>() { return default(T); }
    }
    public interface IMyOtherService { void DoSomethingElse(); }
    public class MyOtherService : IMyOtherService
    {
        public void DoSomethingElse()
        {
            throw new NotImplementedException();
        }
    }
    public class MyService
    {
        private readonly IMyFactory myFactory;
        public MyService(IMyFactory myFactory)
        {
            this.myFactory = myFactory;
        }
        public void MyServiceMethod()
        {
            var thing = myFactory.Get<Mine.Common.MyCommonObject>();
        }
    }

    public interface IMyFactory { T Get<T>(); }

    public class MyDiService
    {
        public FakeContainer Container;
    }
    public class MyFactory : IMyFactory
    {
        private FakeContainer Container;
        public MyFactory(FakeContainer container)
        {
            // obviously this is really a SImple Injector Container
            Container = container;
        }
        public T Get<T>()
        {
            return default(T);
        }
    }
}

namespace Mine.Kernel {
    public interface IMyMultiConcrete { void Do(); }
    public class MyConcreteBase : IMyMultiConcrete
    {
        protected readonly Mine.Services.IMyFactory MyFactory;
        public MyConcreteBase(Mine.Services.IMyFactory myFactory)
        {
            MyFactory = myFactory; 
        }
        public void Do()
        {
            MyFactory.Get<Mine.Common.MyCommonObject>();
        }
    }
    public class MyConcrete1 : MyConcreteBase
    {
        public MyConcrete1(Mine.Services.IMyFactory myFactory) : base(myFactory) {}
        public void Do()
        {
            MyFactory.Get<Mine.Common.MyCommonObject>();
        }
    }
}

namespace Mine.Tasks
{
    public interface IMyTask { void Perform(); }
    public class TaskBase : IMyTask
    {
        protected readonly Mine.Services.IMyOtherService MyOtherService;
        public TaskBase(Mine.Services.IMyFactory myFactory, Mine.Services.IMyOtherService myOtherService)
        {
            MyOtherService = myOtherService;
        }
        public void Perform()
        {
            MyOtherService.DoSomethingElse();
        }
    }

    public class OddTask : TaskBase
    {
        public OddTask(Mine.Services.IMyFactory myFactory, Mine.Services.IMyOtherService myOtherService)
        : base(myFactory, myOtherService) { }


    }


    public class EvenTask : TaskBase
    {
        public EvenTask(Mine.Services.IMyFactory myFactory, Mine.Services.IMyOtherService myOtherService)
        : base(myFactory, myOtherService) { }


    }
}

您所描述的這個IDiFactory抽象不是抽象工廠設計模式的實現——它是服務定位器模式的實現。 但是,Service Locator 是一種反模式,您應該停止使用它,因為它有很多缺點。

相反,類不應該能夠從服務定位器請求一組未綁定的依賴項,但 它們通常 也不應該能夠使用抽象工廠請求一組固定的依賴項。 相反,類應該通過構造函數靜態聲明它們所需的依賴項。

此更改可能已經修復了循環依賴關系,因為您將首先刪除IDiFactory (導致循環)。

DiService 和 DiFactory 與其他服務位於項目 B 中。 並不是說這很重要。

連接依賴項的位置很重要。 依賴關系應該在你的Composition Root 中連接,並且這個 Composition Root 應該存在

盡可能靠近應用程序的入口點。

這很可能意味着您應該將其移動到您的控制台應用程序中。 當您移動該代碼時,只有啟動程序集將依賴於使用的 DI 容器。 那時,將 DI 容器隱藏在抽象后面變得無關緊要(正如您的DiService似乎暗示的那樣)。 不再需要隱藏,因為除了組合根之外,應用程序的其他部分都不知道如何構建依賴圖。 在這一點上,將 DI 容器隱藏在抽象之后不再增加可維護性。

可能有更簡單的方法,但我通常最終做的是有一個單獨的程序集,其中包含我需要注入的所有接口的接口。 主程序集(或 B 可以在您的情況下執行此操作)執行接口到具體實現的綁定,每個人都可以引用接口程序集而無需創建任何循環依賴項。

工廠的接口也應該在那個程序集中。

暫無
暫無

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

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