Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to debug Unity resolution?

In a WPF project(with prism) we are using Unity as DI framework.

Recently, after we merged two big branches, we were not able to start our application, we were having StackOverflowException. Due to the nature of the exception, we were not able to get his call stack or current values, we were only seing that the issue was comming from Unity due to the namespace.

We spent more than 5 hours to find were the issue was occuring:

Simplified:

We were having 3-4 services(A, B, C and D), each one with an implementation, and at some point, the service A was requiring the service B, which was requiring the service C, which was requiring the service D, which was requiring the service A). Basically a cyclic reference.

We would like to know if there is any possibility to add some logs that Unity is trying to solve Service A with the implementation X, and was requiring to resolve service B, ...

This would have helped us a lot to debug this issue, to directly see which services were implicated in this cyclic reference.

Is there anyway to achieve this?

like image 628
J4N Avatar asked Mar 31 '16 11:03

J4N


People also ask

How do I debug a unity project?

Enable script debugging in a Unity player In Unity, open the Build Settings by selecting File > Build Settings. In the Build Settings window, mark the Development Build and Script Debugging checkboxes.


Video Answer


2 Answers

A bit late to the party, but for the exact same problem, I created this and it's working just fine:

internal class LogExtension : UnityContainerExtension
{
    public LogExtension( ILogger logger )
    {
        _logger = logger;
    }

    #region UnityContainerExtension
    protected override void Initialize()
    {
        Context.Strategies.Add( new LoggingStrategy( _logger ), UnityBuildStage.PreCreation );
    }
    #endregion

    #region private
    private readonly ILogger _logger;

    private class LoggingStrategy : BuilderStrategy
    {
        public LoggingStrategy( ILogger logger )
        {
            _logger = logger;
        }

        #region BuilderStrategy
        public override void PreBuildUp( IBuilderContext context )
        {
            _logger.Log( $"Resolving {context.BuildKey.Type} for {context.OriginalBuildKey.Type}" );
        }
        #endregion

        #region private
        private readonly ILogger _logger;
        #endregion
    }
    #endregion
}

And somewhere in the the bootstrapper (ConfigureContainer most likely):

Container.AddExtension( new LogExtension( _logger ) );
like image 188
Haukinger Avatar answered Sep 20 '22 01:09

Haukinger


If You are using Unity v5+ and are too lazy to look up what changed from v4 to v5, then you can copy my version:

using System.Reflection;
using Unity.Extension;
using Unity.Builder;
using Unity.Strategies;

namespace Company.ProjectName.Shared.Bootstrap.Unity
{
    public class LogResolvesUnityContainerExtension : UnityContainerExtension
    {
        private static readonly ILogger Logger = LoggerManager.CreateLogger(MethodBase.GetCurrentMethod().DeclaringType);
        
        protected override void Initialize()
        {
            Context.Strategies.Add(new LoggingStrategy(Logger), UnityBuildStage.PreCreation);
        }

        private class LoggingStrategy : BuilderStrategy
        {
            private readonly ILogger _logger;
            
            public LoggingStrategy(ILogger logger)
            {
                _logger = logger;
            }

            public override void PreBuildUp(ref BuilderContext context)
            {
                // Be aware that for Singleton Resolving this log message will only be logged once, when the Singleton is first resolved. After that, there is no buildup and it is just returned from a cache.
                
                var registrationType = context.RegistrationType; 
                var registrationName = context.Name;
                var resolvedType = context.Type;
                
                var registrationNameWithParenthesesOrNothing = string.IsNullOrEmpty(registrationName) ? "" : $"({registrationName})";
                _logger.LogDebug($"Resolving [{registrationType}{registrationNameWithParenthesesOrNothing}] => [{resolvedType}]");
            }
        }
    }
}

Credit still goes to Haukinger for writing the v4 original version.

like image 20
Jesper Risager Avatar answered Sep 21 '22 01:09

Jesper Risager