简体   繁体   English

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

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

TL;DR. TL;博士。 I have a circular dependency and no idea how to break it.我有一个循环依赖,不知道如何打破它。

Main.csproj: has Program.cs which manually instantiates DiService Main.csproj:具有手动实例化 DiService 的 Program.cs

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

The register method searches CurrentDomain for assemblies and registers collections where multiple implementations exist for a given interface or else registers concretions on a 1-1 basis. register 方法在 CurrentDomain 中搜索程序集并注册集合,其中给定接口存在多个实现,或者在 1-1 的基础上注册具体化。

It then uses the Container to instantiate an abstract factory.然后它使用 Container 来实例化一个抽象工厂。

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

Here's the factory这里是工厂

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

The project dependencies in the solution look like this:解决方案中的项目依赖如下所示:

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

DiService and DiFactory live in project B with the other services. DiService 和 DiFactory 与其他服务位于项目 B 中。 Not that it matters.并不是说这很重要。 I think I'd have the same problem if they were in Main.如果他们在 Main,我想我会遇到同样的问题。

All objects in projects B to E have a constructor injected DiFactory so they can decide what objects they need at run time.项目 B 到 E 中的所有对象都有一个注入了 DiFactory 的构造函数,因此它们可以决定在运行时需要哪些对象。 But for C to make use of it, it must depend on B, which is a circular dependency.但是要让 C 使用它,它必须依赖于 B,这是一个循环依赖。

If I move the DI stuff to a new project F, then all projects can depend on that but how does the factory reference the types in the other projects without creating another circular dependency?如果我将 DI 的东西移动到一个新项目 F,那么所有项目都可以依赖它,但是工厂如何在不创建另一个循环依赖的情况下引用其他项目中的类型?

I followed the documentation for IRequestHandler, I just didn't do the dictionary.我遵循了 IRequestHandler 的文档,我只是没有做字典。 Most likely I have a design flaw but I can't see what it is.很可能我有一个设计缺陷,但我看不到它是什么。

Here's an example of the interactions between objects for LinqPad - doesn't compile but it looks right.这是 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) { }


    }
}

This IDiFactory abstraction you are describing is not an implementation of the Abstract Factory design pattern—it is an implementation of the Service Locator pattern.您所描述的这个IDiFactory抽象不是抽象工厂设计模式的实现——它是服务定位器模式的实现。 Service Locator, however, is an anti-pattern and you should stop using it because its numerous downsides.但是,Service Locator 是一种反模式,您应该停止使用它,因为它有很多缺点。

Instead, classes should not be able to request an unbound set of dependencies from a Service Locator, but neither should they typically be able to request a fixed set of dependencies using an Abstract Factory.相反,类不应该能够从服务定位器请求一组未绑定的依赖项,但 它们通常 也不应该能够使用抽象工厂请求一组固定的依赖项。 Instead, classes should statically declare their required dependencies through the constructor.相反,类应该通过构造函数静态声明它们所需的依赖项。

This change might already fix the circular dependency as you will remove the IDiFactory (that is causing the cycle) in the first place.此更改可能已经修复了循环依赖关系,因为您将首先删除IDiFactory (导致循环)。

DiService and DiFactory live in project B with the other services. DiService 和 DiFactory 与其他服务位于项目 B 中。 Not that it matters.并不是说这很重要。

It does matter where you wire up your dependencies.连接依赖项的位置很重要。 Dependencies should be wired up in your Composition Root and this Composition Root should live依赖关系应该在你的Composition Root 中连接,并且这个 Composition Root 应该存在

As close as possible to the application's entry point.尽可能靠近应用程序的入口点。

This most likely means that you should move this to your Console application.这很可能意味着您应该将其移动到您的控制台应用程序中。 When you move that code, only the start-up assembly will take a dependency on the used DI Container.当您移动该代码时,只有启动程序集将依赖于使用的 DI 容器。 At that point, it becomes irrelevant to hide the DI Container behind an Abstraction (as your DiService seems to imply).那时,将 DI 容器隐藏在抽象后面变得无关紧要(正如您的DiService似乎暗示的那样)。 Hiding is not needed anymore, because no other parts of the application except the Composition Root will have any knowledge about how dependency graphs are built.不再需要隐藏,因为除了组合根之外,应用程序的其他部分都不知道如何构建依赖图。 Hiding the DI Container behind an abstraction, at that point, doesn't increase maintainability any longer.在这一点上,将 DI 容器隐藏在抽象之后不再增加可维护性。

There might be an easier way, but what I typically end up doing is to have a separate assembly containing interfaces for everything I need to inject.可能有更简单的方法,但我通常最终做的是有一个单独的程序集,其中包含我需要注入的所有接口的接口。 The main assembly (or B could do it in your case) performs the binding of interfaces to concrete implementations and everyone can reference the interfaces assembly without creating any circular dependencies.主程序集(或 B 可以在您的情况下执行此操作)执行接口到具体实现的绑定,每个人都可以引用接口程序集而无需创建任何循环依赖项。

The interface for the factory should also be in that assembly.工厂的接口也应该在那个程序集中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM