在 autofac 中可视化依赖树深度

Visualizing dependency tree depth in autofac

我可能一直在搜索错误的东西,在错误的框中查找。但是我似乎找不到一种好方法来可视化我的依赖树在 C# 中的深度。

我最初只是尝试连接 Autofac 的准备事件和激活事件。但我不知道这是否足够好。结果看起来有点古怪。似乎准备事件启动得太频繁了。而且激活事件似乎只有在实际创建新对象时才会激活。

我们的代码是.NET 4.7.2 我们使用 Autofac 作为我们的容器来处理依赖注入。

有没有人知道我们如何可视化深度?也许有一些好的代码或产品可以帮助我们?

前段时间,我在Autofac上有过类似的任务。我最终得到的结果如下:

public class DebugResolveModule : Module
{
    private readonly ThreadLocal<ResolveInfo> _current = new ThreadLocal<ResolveInfo>();

    protected override void AttachToComponentRegistration(
        IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += Registration_Preparing;
        registration.Activating += Registration_Activating;

        base.AttachToComponentRegistration(componentRegistry, registration);
    }

    private void Registration_Preparing(object sender, PreparingEventArgs e)
    {
        _current.Value = new ResolveInfo(e.Component.Activator.LimitType, _current.Value);
    }

    private void Registration_Activating(object sender, ActivatingEventArgs<object> e)
    {
        var current = _current.Value;
        current.MarkComponentAsResolved();
        _current.Value = current.Parent;

        if (current.Parent == null)
        {
            VisualizeGraph(current);
            Debug.WriteLine(
                $"total resolve time: {current.ResolveTime.TotalMilliseconds} ms.");
        }
    }

    private static void VisualizeGraph(ResolveInfo node, int depth = 0)
    {
        for (int i = 0; i < depth; i++)
        {
            Debug.Write("   ");
        }

        Debug.Write(node.ComponentType);
        Debug.Write(" (");
        Debug.Write(node.ResolveTime.TotalMilliseconds.ToString("F1"));
        Debug.Write(" ms. / ");
        Debug.Write(node.CreationTime.TotalMilliseconds.ToString("F1"));
        Debug.Write(" ms.)");


        Debug.WriteLine("");

        foreach (var dependency in node.Dependencies)
        {
            VisualizeGraph(dependency, depth + 1);
        }
    }

    private sealed class ResolveInfo
    {
        private Stopwatch _watch = Stopwatch.StartNew();

        public ResolveInfo(Type componentType, ResolveInfo parent)
        {
            ComponentType = componentType;
            Parent = parent;
            Dependencies = new List<ResolveInfo>(4);

            if (parent != null)
            {
                parent.Dependencies.Add(this);
            }
        }

        public Type ComponentType { get; }

        // Time it took to create the type including its dependencies
        public TimeSpan ResolveTime { get; private set; }

        // Time it took to create the type excluding its dependencies
        public TimeSpan CreationTime { get; private set; }
        public ResolveInfo Parent { get; }

        public List<ResolveInfo> Dependencies { get; }

        public void MarkComponentAsResolved()
        {
            ResolveTime = _watch.Elapsed;
            CreationTime = ResolveTime;

            foreach (var dependency in this.Dependencies)
            {
                CreationTime -= dependency.ResolveTime;
            }
                
            _watch = null;
        }
    }
}

请注意,这并不是您想要的,因为它是一个解决触发器。这意味着 SingleInstance 仅解析一次,这意味着您下次请求图形时,您将丢失单例。这对我来说不是问题,因为我使用这段代码来检测对象图的缓慢解析部分。

但是,此代码可能仍会为您提供有关如何执行此操作的一些想法。