Detecting the cause of a circular dependency in Unity
Asked Answered
H

1

10

Is it possible to configure Unity to either detect a circular reference or to intercept the type resolver to display some debugging information?

Example

Here are a couple of interfaces and classes which are dependent upon each other

public interface IThing1 { }

public class Thing1 : IThing1
{
    private IThing2 _thing2;

    public Thing1(IThing2 thing2)
    {
        _thing2 = thing2;
    }

}

public interface IThing2 { }

public class Thing2 : IThing2
{
    private IThing1 _thing1;

    public Thing2(IThing1 thing1)
    {
        _thing1 = thing1;
    }
}

Castle Windsor

If these two types are configured in Castle Windsor it will throw an exception and provide some debug information to find the circular reference:

Castle.MicroKernel.CircularDependencyException: Dependency cycle has been detected when trying to resolve component 'CircularIoC.Thing1'.
The resolution tree that resulted in the cycle is the following:
Component 'CircularIoC.Thing1' resolved as dependency of
    component 'CircularIoC.Thing2' resolved as dependency of
    component 'CircularIoC.Thing1' which is the root component being resolved.

Unity

If Unity is configured to resolve these types like so

    private static void ResolveWithUnity()
    {
        var container = new UnityContainer();

        container.RegisterType<IThing1, Thing1>();
        container.RegisterType<IThing2, Thing2>();

        var thing = container.Resolve<IThing1>();

        container.Dispose();
    }

The call to container.Resolve<> will cause a StackOverflowException.

This is the documented behaviour but it would be nice to have some more useful information. Is there any customisation that will provide more information about the circular reference?

Alternatively is there any way to hook in to the type resolver process to emit some debugging information? I am thinking of decorating the main type resolver to output the name of the type being resolved. This will provide some feedback and a pointer to dependency that is causing the circular reference.

While I know that changing to a different IoC would solve the problem, this is not unfortunately an option.

Hormonal answered 19/3, 2013 at 11:3 Comment(4)
Only Castle Windsor, Autofac and Simple Injector throw meaningful errors. All other frameworks throw StackOverflowExceptions, which is actually a really bad thing to do. I'd bet adding such feature can be pretty daunting when support is not built-in. Besides, these are the only three containers that contain 'profiling/diagnostic services' to help you spot container misconfigurations. It's a shame you can't switch.Raillery
Thanks Steven. Out of curiosity, just StructureMap provide this kind of debugging information?Hormonal
As far as I know it doesn't. Here's some documentation about diagnosing problems with StructureMap, but IFAIK there's nothing more built in than the AssertConfigurationIsValid. Besides such feature, Castle (debug views) and Simple Injector (diagnostic services) can detect whether there are lifestyle mismatches (which is an important source of bugs). Castle unfortunately only only supports singleton -> web request and singleton -> web request checks, while Simple Injector detects much more.Raillery
Ninject also provides a meaningful exception including the activation path if you use Injection Constructor pattern.Spendthrift
V
1

Unity sadly doesn't support this (incredibly important) feature. If you are willing to put your back into it, you can implement a smart decorator using some elbow grease.

What you will need is to override all Registration methods and build and update a data structure for the dependencies (dependency graph). Then write a method that preforms DFS to detect a circular dependency, you can either use it as a finalizer for the registration process, that will detect pre-resolving if circular dependencies are possible, or use it per resolve for the specific type requested.

As you can see, it's a lot of work...

Another option, is just to wrap up with a decorator the resolve methods and catch the StackOverflowException, analyze it to make sure it resulted from the resolving process, and build a proper circular dependency exception.

Vaticination answered 21/12, 2014 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.