Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Visualizing dependency tree depth in autofac

I may have been searching for the wrong stuff, looking in the wrong boxes. But I can't seem to find a good way to visualize how deep my dependecy tree is in C#.

I've initially tried just hooking up on the preparing-event & the activating-event from Autofac. But I can't figure out if this is good enough. The results looks kinda funky. It seems that the preparing-event is initiated too often. And it seems that the activating event is only activated when a new object is actually created.

Our code is .NET 4.7.2 We're using Autofac as our container to handle the dependency injection.

Does anyone have a good idé for how we can visualize the depth? Maybe there exists some good code or a product out there that can help us?

like image 652
Squazz Avatar asked Dec 09 '19 10:12

Squazz


People also ask

Whats Autofac?

Autofac is an addictive IoC container for . NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. This is achieved by treating regular . NET classes as components.

How Autofac works?

Autofac allows you to specify how many instances of a component can exist and how they will be shared between other components. Controlling the scope of a component independently of its definition is a very important improvement over traditional methods like defining singletons through a static Instance property.

Why to use Autofac?

AutoFac provides better integration for the ASP.NET MVC framework and is developed using Google code. AutoFac manages the dependencies of classes so that the application may be easy to change when it is scaled up in size and complexity. Let us peek into why the built-in DI container is being replaced by AutoFac.

What is Autofac .NET core?

Autofac is a . Net-based IoC container. When classes interact with one another, it manages the dependencies between them that allow applications to remain flexible as they grow in size and complexity. Autofac is the most widely used DI/IoC container for ASP.NET, and it is fully compatible with.NET Core as well.

What is Autofac dependency injection?

Using the Autofac dependency injection container to simplify the configuration of object-oriented applications. Where to Next? Autofac is an open-source dependency injection (DI) or inversion of control (IoC) container developed on Google Code.

How do you add Autofac references to a container?

Add Autofac references. At application startup… Create a ContainerBuilder. Register components. Build the container and store it for later use. During application execution… Create a lifetime scope from the container. Use the lifetime scope to resolve instances of the components.

How to visualize dependency graph in simple injector?

The implementation idea of dependency graph visualization is quite simple, as the biggest chunk of work is done by Simple Injector itself. Here are the steps: Run all your DI registrations as you do in the actual application. This will initialize Container to the desired state.

What is Autofac?

Autofac is an open-source dependency injection (DI) or inversion of control (IoC) container developed on Google Code. Autofac differs from many related technologies in that it sticks as close to bare-metal C# programming as possible.


1 Answers

Some time ago, I had a similar quest with Autofac. What I ended up with was the following:

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;
        }
    }
}

Do note that this does not exactly what you want, because it is a resolve trigger. This means that a SingleInstance is only resolved once, which means the next time you request the graph, you'll be missing the singletons. This wasn't a problem for me, because I used this code to detect slow-resolving parts of the object graphs.

This code might, however, still give you some ideas of how to do this.

like image 115
Steven Avatar answered Sep 20 '22 11:09

Steven