Skip to content

Performance issue when querying varbinary(MAX), varchar(MAX), nvarchar(MAX) or XML with Async #18221

@tibitoth

Description

@tibitoth

Description

I would like to resurrect the following EF6 issue: dotnet/ef6#88 because it's still present in EF Core 2.2 and 3.0 also.

There's a huge performance issue when querying a table containing varbinary(MAX) columns with .ToListAsync(). The problem is EF, despite the presence of varbinary(max) column, uses CommandBehavior.Default with ExecuteReaderAsync(). (instead of CommandBehavior.SequentialAccess)

Related SO question and answer: https://stackoverflow.com/questions/28543293/entity-framework-async-operation-takes-ten-times-as-long-to-complete

This is a huge issue, we cannot use ToList as a workaround because this requires impossible checks at development time.

Steps to reproduce

I have created a small repro codebase and benchmarks.

public class AppDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=EFCore-repro-ToListAsync;Trusted_Connection=True;");
    }

    public DbSet<TestEntity> Blobs { get; set; }
}

public class TestEntity
{
    public int Id { get; set; }
    public string LargeTextBlob { get; set; }
}

public class Program
{
    static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<Benchmarks>();
        Console.ReadKey();
    }
}

public class Benchmarks
{
    private int id;

    public Benchmarks()
    {
        using (var context = new AppDbContext())
        {
            var array = new char[5 * 1024 * 1024];
            var random = new Random();
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = (char)random.Next(32, 126);
            }

            var entity = new TestEntity { LargeTextBlob = new string(array) };
            context.Blobs.Add(entity);
            context.SaveChanges();

            id = entity.Id;
        }
    }

    [Benchmark]
    public void GetWithToList()
    {
        using (var context = new AppDbContext())
        {
            var entity = context.Blobs.Where(b => b.Id == id).ToList();
        }
    }

    [Benchmark]
    public async Task GetWithToListAsync()
    {
        using (var context = new AppDbContext())
        {
            var entity = await context.Blobs.Where(b => b.Id == id).ToListAsync();
        }
    }
}

Benckmarks

Method Mean Error StdDev
GetWithToList 45.63 ms 0.4871 ms 0.4318 ms
GetWithToListAsync 15,088.37 ms 178.2588 ms 158.0218 ms

Further technical details

EF Core version: 2.2, 3.0
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (e.g. .NET Core 3.0, .NET Core 2.2)
Operating system: Windows 10.
IDE: (e.g. Visual Studio 2019 16.3)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions