控制台应用程序中的Simple Injector,具有多个项目和一个抽象工厂



TL;DR。我有一个循环依赖,不知道如何打破它。

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中。这并不重要。如果他们在缅因州,我想我也会遇到同样的问题。

项目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抽象不是抽象工厂设计模式的实现,而是服务定位器模式的实现。然而,ServiceLocator是一种反模式,您应该停止使用它,因为它有很多缺点。

相反,类不应该能够从服务定位器请求一组未绑定的依赖项,但它们通常也不应该能够使用抽象工厂请求一组固定的依赖项。相反,类应该通过构造函数静态地声明它们所需的依赖关系。

此更改可能已经修复了循环依赖关系,因为您将首先删除IDiFactory(导致循环)。

DiService和DiFactory与其他服务一起生活在项目B中。这并不重要。

在哪里连接依赖关系是很重要的。依赖项应该在你的组合根中连接起来,这个组合根应该是

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

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

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

工厂的接口也应该在该组件中。

最新更新