Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add API to analyse object composition time #722

Open
dotnetjunkie opened this issue Jun 4, 2019 · 0 comments

Comments

Projects
None yet
1 participant
@dotnetjunkie
Copy link
Collaborator

commented Jun 4, 2019

When it comes to constructing object graphs of large applications, it can get hard to keep track of what developers check in and spot when they accidentally create constructors that do too much.

Simple Injector could help in this regard by doing an optional scan of the complete object graph to measure the time it takes construct graphs. This analysis can be reported back to the user in some form. This is especially useful in integration testing or exploratory testing scenarios, where a developer likes to detect or prevent performance problems, caused by constructors that do I/O or other heavy lifting.

I'm still unsure what the correct API for this would be and that the APIs entry point should be.

Simple Injector currently contains one interception point that allows you to measure performance, which is Container.Options.RegisterResolveInterceptor, but this interception point allows only root objects to be measured, for instance:

var resolves = new List<(InitializationContext context, TimeSpan elapsed)>();

container.Options.RegisterResolveInterceptor((context, producer) =>
    {
        var watch = Stopwatch.StartNew();
        try
        {
            return producer();
        }
        finally
        {
            resolves.Add((context, watch.Elapsed));
        }
    },
    c => true);

container.Verify();

var slowestResolves =
    from resolve in resolves
    where resolve.elapsed > TimeSpan.FromMilliseconds(10)
    orderby resolve.elapsed descending
    select resolve;

if (slowestResolves.Any())
{
    Assert.Fail("The following components where slow in their creation:\n * " +
        string.Join(Environment.NewLine + " * ",
            from resolve in slowestResolves
            select resolve.context.Registration.ImplementationType.ToFriendlyName() +
                $" ({resolve.elapsed.TotalMilliseconds} ms.)"));
}

This code, however, only plugs into the root objects, while the real culprit might lie in an object very deep in the object graph. The real cause will be hard to track.

There is an Container.RegisterInitializer overload that allows to hook into individual registrations, but this method, however, only gets triggered after the object is created. This, therefore, isn't helpful.

The only feasible way to plug in, is to hook onto the ExpressionBuilding event, and replace the built Expression with one that adds measurement. This, however, is cumbersome. It would be much nicer if such feature is supported out-of-the-box.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.