带有多个项目和一个抽象工厂的控制台应用程序中的简单注入器

时间:2019-01-25 20:25:38

标签: c# dependency-injection simple-injector

TL; DR。我有一个循环依赖,不知道如何解决。

Main.csproj:具有可手动实例化DiService的Program.cs

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

register方法在CurrentDomain上搜索程序集,并在给定接口存在多个实现的情况下注册集合,或者以1-1为基础注册concret。

然后使用容器实例化抽象工厂。

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) { }


    }
}

2 个答案:

答案 0 :(得分:1)

您要描述的IDiFactory抽象不是抽象工厂设计模式的实现,而是服务定位器模式的实现。但是,服务定位器为is an anti-pattern,您应该停止使用它,因为它有很多缺点。

相反,类不应能够从Service Locator请求无约束的一组依赖关系,但是neither should they通常能够使用Abstract Factory请求一组固定的依赖关系。相反,类应通过构造函数静态声明其所需的依赖关系。

此更改可能已经解决了循环依赖性,因为您将首先删除IDiFactory(这会导致循环)。

  

DiService和DiFactory与其他服务一起位于项目B中。没关系。

在哪里连接依赖关系并不重要。依赖关系应该连接到您的Composition Root中,并且此组合根应该存在

  

尽可能靠近应用程序的入口点。

这很可能意味着您应该将其移至控制台应用程序。移动该代码时,只有启动程序集将依赖于所使用的DI容器。那时,将DI容器隐藏在抽象后面就变得无关紧要了(正如您的DiService所暗示的那样)。不再需要隐藏,因为除了“合成根”之外,应用程序的其他任何部分都不会具有有关如何构建依赖图的知识。那时,将DI容器隐藏在抽象的后面并不会再增加可维护性。

答案 1 :(得分:0)

可能有一种更简单的方法,但是我通常最终要做的是拥有一个单独的程序集,其中包含我需要注入的所有内容的接口。 主程序集(或B可以完成)将接口绑定到具体的实现,每个人都可以引用接口程序集而无需创建任何循环依赖项。

工厂的接口也应该在该程序集中。