繁体   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