C# 如何调试统一解析?

C# 如何调试统一解析?,c#,wpf,dependency-injection,unity-container,prism,C#,Wpf,Dependency Injection,Unity Container,Prism,在WPF项目(使用prism)中,我们使用Unity作为DI框架 最近,在我们合并了两个大分支之后,我们无法启动我们的应用程序,我们遇到了StackOverflowException。由于异常的性质,我们无法获得他的调用堆栈或当前值,我们只知道由于名称空间的原因,问题来自Unity 我们花了5个多小时来查找问题发生的原因: 简化: 我们有3-4个服务(A、B、C和D),每个服务都有一个实现,在某个时候,服务A需要服务B,服务B需要服务C,服务D需要服务A)。基本上是循环引用 我们想知道是否有可能

在WPF项目(使用prism)中,我们使用
Unity
作为DI框架

最近,在我们合并了两个大分支之后,我们无法启动我们的应用程序,我们遇到了
StackOverflowException
。由于异常的性质,我们无法获得他的调用堆栈或当前值,我们只知道由于名称空间的原因,问题来自Unity

我们花了5个多小时来查找问题发生的原因:

简化:

我们有3-4个服务(A、B、C和D),每个服务都有一个实现,在某个时候,服务A需要服务B,服务B需要服务C,服务D需要服务A)。基本上是循环引用

我们想知道是否有可能添加一些日志,
Unity
正试图用实现X解决服务A,并要求解决服务B

这将有助于我们调试这个问题,直接查看这个循环引用涉及哪些服务


是否仍然可以实现此目的?

您可以配置程序集绑定日志查看器以帮助诊断有问题的Unity解决方案

它与Visual Studio一起安装,您只需在VS命令提示符中键入“fuslogvw”即可启动


否则,这可能有助于配置日志记录程序:

会议推迟了一点,但对于完全相同的问题,我创建了这个,它工作正常:

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
}
在引导程序中的某个位置(
ConfigureContainer
最有可能):


如果您正在使用Unity v5+,并且懒得查找从v4到v5的更改内容,则可以复制我的版本:

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

Haukinger编写v4原始版本仍然值得称赞。

在解析类型时,Unity中没有跟踪或输出。也许您可以使用自定义扩展来扩展Unity,以实现所需的行为。Fusion日志记录似乎无助于调试Unity解决方案问题。我在哪里可以找到IBuilderContext接口?我的代码是针对Unity 4的。在版本5中,
BuilderStrategy
的签名变化最小,但基本概念仍然有效。
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}]");
            }
        }
    }
}