如何解决循环依赖 [英] How to Solve Circular Dependency

查看:109
本文介绍了如何解决循环依赖的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的代码结构有问题,它以某种方式进入了循环依赖。这是我的代码的解释:

Hi I have a problem with the structure of my code, it somehow goes into Circular Dependency. Here is an explanation of how my code looks like:

我有一个 ProjectA ,其中包含 BaseProcessor BaseProcessor ProjectB 中引用了名为 Structure 的类。在 BaseProcessor 中,有一个 Structure 实例作为变量。

I have a ProjectA contains BaseProcessor and BaseProcessor has a reference to a class called Structure in ProjectB. Inside BaseProcessor, there is an instance of Structure as a variable.

projectB中 还有其他一些类,例如定价交易等。
ProjectB 具有一个名为 BaseStructure 的基类,即 Structure 定价 Transaction 类,它们均从 BaseStructure
现在在定价 Transaction 类中,我想从 BaseStructure 调用 BaseProcessor 类中的方法导致循环依赖的类。

In projectB there are someother classes such as Pricing, Transaction etc. Every class in ProjectB has a base class called BaseStructure i.e. Structure, Pricing and Transaction classes all inherited from BaseStructure. Now in Pricing and Transaction classes, I want to call a method in BaseProcessor class from BaseStructure class which causing Circular Dependency.

我尝试过的是:


使用Unity,但我不知道如何使其起作用,因为我尝试使用以下功能:
unityContainer.ReferenceType(IBaseProcessor,BaseProcessor)
BaseStructure 中,那么它需要一个 BaseProcessor 引用。 strong>这也会导致循环依赖。

What I have tried is:
Using Unity, but I didn't figure out how to make it work because I try to use function like: unityContainer.ReferenceType(IBaseProcessor, BaseProcessor) in BaseStructure then it will need a reference of BaseProcessor which also cause Circular Dependency.

我还尝试创建 IBaseProcessor 的接口并创建一个函数(我想要的函数调用)在此接口中进行声明。并让 BaseProcessor BaseStructure 都继承此接口。但是如何在 Pricing Transaction 类中调用该函数而不创建 BaseProcessor 的实例?

And I've also tried creating an interface of IBaseProcessor and create a function(the function I want to call) declaration in this interface. And let both BaseProcessor and BaseStructure inherit this interface. But how can I call the function in Pricing and Transaction class without create an instance of BaseProcessor?

除了使用反射之外,还有谁能告诉我如何解决此问题?

Can anyone please tell me how to resolve this problem other than using reflection?

任何帮助将不胜感激。谢谢:)

Any help will be much appreciated. Thanks :)

推荐答案

您的DI容器不能帮助解决循环引用,因为它是应用程序的依赖结构防止创建对象。即使没有DI容器,也没有一些特殊的技巧就无法构造对象图。

Your DI container can't help solving the circular reference, since it is the dependency structure of the application that prevents objects from being created. Even without a DI container, you can't construct your object graphs without some special 'tricks'.

请注意,在大多数情况下,循环依赖图形是应用程序中设计缺陷的标志,因此您可能需要考虑仔细研究一下设计,看看是否无法通过将逻辑提取到单独的类中来解决此问题。

Do note that in most cases cyclic dependency graphs are a sign of a design flaw in your application, so you might want to consider taking a very close look at your design and see if this can't be solved by extracting logic into separate classes.

但是,如果这不是一种选择,则基本上有两种方法可以解决此循环依赖图。您要么需要回退到属性注入,要么需要推迟使用工厂 Func< T> 来解析组件,或者像@onof建议使用 Lazy< T>

But if this is not an option, there are basically two ways of resolving this cyclic dependency graph. Either you need to fallback to property injection, or need to postpone resolving the component with a factory, Func<T>, or like @onof proposed with a Lazy<T>.

在这两种风格中,有很多可能的方法,例如回退到属性注入到您的应用程序中(对不起,我的C#):

Within these two flavors, there are a lot of possible ways to do this, for instance by falling back to property injection into your application (excuse my C#):

public class BaseStructure {
    public BaseStructure(IDependency d1) { ... }

    // Break the dependency cycle using a property
    public IBaseProcessor Processor { get; set; }
}

这将移动 IBaseProcessor 从构造函数到属性的依赖关系,并允许您在构造图形之后进行设置。以下是手动生成的对象图的示例:

This moves the IBaseProcessor dependency from the constructor to a property and allows you to set it after the graph is constructed. Here's an example of an object graph that is built manually:

var structure = new Structure(new SomeDependency());
var processor = new BaseProcessor(structure);

// Set the property after the graph has been constructed.
structure.Processor = processor;

更好的选择是将属性隐藏在组合根。因为您可以继续使用构造函数注入,所以这使您的应用程序设计更简洁。示例:

A better option is to hide the property inside your Composition Root. This makes your application design cleaner, since you can keep using constructor injection. Example:

public class BaseStructure {
    // vanilla constructor injection here
    public BaseStructure(IDependency d1, IBaseProcessor processor) { ... }
}

// Defined inside your Composition Root.
private class CyclicDependencyBreakingProcessor : IBaseProcessor {
    public IBaseProcessor WrappedProcessor { get; set; }

    void IBaseProcessor.TheMethod() {
        // forward the call to the real processor.
        this.WrappedProcessor.TheMethod();
    }
}

现在而不是注入在您的结构中将BaseProcessor 注入到 CyclicDependencyBreakingProcessor 中,该代码将在图的构造:

Now instead of injecting the BaseProcessor into your Structure, you inject the CyclicDependencyBreakingProcessor, which will be further initialized after the construction of the graph:

var cyclicBreaker = new CyclicDependencyBreakingProcessor();
var processor = new BaseProcessor(new Structure(new SomeDependency(), cyclicBreaker));

// Set the property after the graph has been constructed.
cyclicBreaker.WrappedProcessor = processor;

这与以前基本相同,但是现在应用程序由于存在一个

This is basically the same as before, but now the application stays oblivious from the fact that there is a cyclic dependency that needed to be broken.

除了使用属性注入,还可以使用 Lazy< T> ,但就像进行属性注入一样,最好将此实现细节隐藏在您的合成根中,不要让 Lazy< T> 值泄漏到您的应用程序中,因为这只会增加应用程序的噪音,从而使您的代码更复杂且更难测试。此外,应用程序不必在乎依赖项注入是否延迟。与 Func< T> (和 IEnumerable< T> )一样,在注入时Lazy< T< code>依赖项是在考虑特定实现的情况下定义的,我们在泄漏实现细节。因此最好执行以下操作:

Instead of using property injection, you can also use Lazy<T>, but just as with the property injection, it is best to hide this implementation detail inside your Composition Root, and don't let Lazy<T> values leak into your application, since this just adds noise to your application, which makes your code more complex and harder to test. Besides, the application shouldn't care that the dependency injection is delayed. Just as with Func<T> (and IEnumerable<T>), when injecting a Lazy<T> the dependency is defined with a particular implementation in mind and we're leaking implementation details. So it's better to do the following:

public class BaseStructure {
    // vanilla constructor injection here
    public BaseStructure(IDependency d1, IBaseProcessor processor) { ... }
}

// Defined inside your Composition Root.
public class CyclicDependencyBreakingProcessor : IBaseProcessor {
    public CyclicDependencyBreakingBaseProcessor(Lazy<IBaseProcessor> processor) {...}

    void IBaseProcessor.TheMethod() {
        this.processor.Value.TheMethod();
    }
}

使用以下连线:

IBaseProcessor value = null;

var cyclicBreaker = new CyclicDependencyBreakingProcessor(
    new Lazy<IBaseProcessor>(() => value));
var processor = new BaseProcessor(new Structure(new SomeDependency(), cyclicBreaker));

// Set the value after the graph has been constructed.
value = processor;   

到目前为止,我只展示了如何手动构建对象图。使用DI容器执行此操作时,通常希望让DI容器为您构建完整的图形,因为这会产生更易于维护的合成根。但这会使打破循环依赖关系变得有些棘手。在大多数情况下,诀窍是用缓存的方式注册您想破坏的组件(基本上不是瞬时的)。例如,每个网络请求的生活方式。

Up until now I only showed how to build up the object graph manually. When doing this using a DI container, you usually want to let the DI container build up the complete graph for you, since this yields a more maintainable Composition Root. But this can make it a bit more tricky to break the cyclic dependencies. In most cases the trick is to register the component that you want to break with a caching lifestyle (basically anything else than transient). Per Web Request Lifestyle for instance. This allows you to get the same instance in a lazy fashion.

使用最后一个 CyclicDependencyBreakingProcessor 示例,我们可以创建一个实例。以下Unity注册:

Using the last CyclicDependencyBreakingProcessor example, we can create the following Unity registration:

container.Register<BaseProcessor>(new PerRequestLifetimeManager());
container.RegisterType<IStructure, Structure>();
container.RegisterType<IDependency, SomeDependenc>();
container.Register<IBaseProcessor>(new InjectionFactory(c =>
    new CyclicDependencyBreakingProcessor(
        new Lazy<IBaseProcessor>(() => c.GetInstance<BaseProcessor>())));

这篇关于如何解决循环依赖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆