Skip to content

asp.net project upgrade from 2.2 to dotnet 5 drastically reduced DI resolve speed #51633

@lansman

Description

@lansman

I'm not sure my problem related to Dependency Injection, AutoFac, EF Core or dotnet. But stack trace gives me Microsoft.Extensions.DependencyInjection so i tend to send it here.

I had dotnet core webapi 2.2 project. Have updated it and all libraries to dotnet 5.0 and got drastically reduced perfomance on all DI resolve actions, but the most important problem with slow resolving DbContext.

I have 2 DbContext: RW and RO. RW Is initialized with AddDbContext function with singleton DbContextOptionsReader. RO context is initialized with Scoped instance of its OptionsReader. It gives huge resolve speed difference. If register it as Singleton as well - speed becomes the same.

Startup.cs

public override void ConfigureCollection()
{
    ServiceCollection.AddSingleton<DbConnectionOptionsReader>();
    ServiceCollection.AddScoped<DbRoConnectionOptionsReader>(); // i need it to be scoped

    ServiceCollection.AddDbContext<DbContext>(SettingsRw); 
    ServiceCollection.AddDbContext<DbContextRo>(SettingsRo);

    private void SettingsRw(IServiceProvider provider, DbContextOptionsBuilder builder)
    {
        var dbOptionsReader = provider.GetRequiredService<DbConnectionOptionsReader>();
        builder.UseNpgsql(dbOptionsReader.ReadConnectionString(),
            opts =>
            {
               // some code
            });            
    }
    private void SettingsRo(IServiceProvider provider, DbContextOptionsBuilder builder)
    {
        var dbOptionsReader = provider.GetRequiredService<DbRoConnectionOptionsReader>();
        builder.UseNpgsql(dbOptionsReader.ReadConnectionString(), opts =>
        {
            // some code
        }
    }
}
public class DbConnectionOptionsReader
{
     // 2 dependecies only, both are Singletones
    public DbConnectionOptionsReader(IEnvContext envContext, IConfigurationRoot configRoot)
    {
        EnvContext = envContext;
        ConfigRoot = configRoot;
    }
    
    // skipped
}

public class DbRoConnectionOptionsReader : DbConnectionOptionsReader, IGetRoConnection
{
    // lot of dependencies
    public DbRoConnectionOptionsReader(IEnvContext envContext,
        IConfigurationRoot configRoot,
        IDoAllowRoRequest doAllowRoRequest,
        ILogger<DbRoConnectionOptionsReader> logger,
        IGetReplicationConnection getReplicationConnection,
        IGetRwForcer getRwForcer) : base(envContext, configRoot)
    {
        _doAllowRoRequest = doAllowRoRequest;
        _logger = logger;
        _getReplicationConnection = getReplicationConnection;
        _getRwForcer = getRwForcer;
    }
    
    // skipped
}

Test code:

[Route("api/Sandbox3/[action]")]
public class Sandbox3Controller : Controller
{
    private readonly IServiceProvider _serviceProvider;

    public Sandbox3Controller(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    // GET
    public IActionResult MeasureDbContextTime()
    {
        var dt1 = DateTime.Now;
        var _ = _serviceProvider.GetRequiredService<DbContext>().SystemParameter.Count();
        var dt2 = DateTime.Now - dt1;

        var dt11 = DateTime.Now;
        var __ = _serviceProvider.GetRequiredService<DbContextRo>().SystemParameter.Count();
        var dt22 = DateTime.Now - dt11;
        return Ok(
            new
            {
                DbRwTime = dt2,
                DbRoTime = dt22
            });
    }
}

Results:

dotnet 2.2 before upgrade

{
    "DbRwTime": "00:00:00.0079315",
    "DbRoTime": "00:00:00.0079183"
}

Perfect!

Upgraded 2.2 to dotnet5 project

{
    "DbRwTime": "00:00:00.0429624",
    "DbRoTime": "00:00:00.2241661" -- RO is very slow
}

RW x6 times slower!
RO x32 times slower!!

For experiment i created fresh dotnet 5 project and copypasted necessary parts for run (program.cs, startup.cs, both dbcontexts)

{
    "DbRwTime": "00:00:00.0198894",
    "DbRoTime": "00:00:00.0260551",
    "ResolveTime": "00:00:00.0000769"
}

RW x2 times slower than 2.2
RO x4 times slower than 2.2

But RO is still much better than my upgraded 5.0 project, 0.02 sec vs 0.2 sec.

I'd like to achive clean 5.0 results at least for the first time. So i have to understand why RO is so low on upgraded project.
I've measured traces via jetbrains dotpeak:

Here is comparison of stack traces of DbContext's Count() function
image

You can see fast method (upper) goes via TryAddCoreServices and ends pretty fast
Slow method (lower) goes via DynamicServiceProviderEngine and lasts so long.

Tell me please, in which conditions code may go 1 or 2 way?

I use Autofac and part of registrations goes via Autofac, another part - via IServiceCollection

dotnet 5.0.5 x64
Entity Framework 5.0.5
Autofac 6.1.0
Autofac.Extensions.DependencyInjection 7.1.0

Debug/Release project mode doesnt matter. I've tried downgrade to dotnet 3.1 and the problem is still there. So it occured during 2.2 -> 3.1 transition

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions