Entity Framework 6 vs Entity Framework Core 3: Comparing Performance

by Chad
Published December 02, 2019
Last updated June 26, 2020

Waves Waves

Entity Framework (EF) Core was a complete rewrite from the tried and tested EF6. One of the most touted benefits EF Core has over EF6 is improved performance. Using real benchmarks, I will use worked examples to demonstrate whether Entity Framework 6 or Entity Framework Core performs the best.

Setting Up the Benchmark

To set up this benchmark, I will create two .NET Core 3 class libraries. Each class library will contain a package reference to EF6 and EF Core 3 respectively. Finally, each of these class libraries, which contain a functionally equivalent DbContext and data model, will be tested against each other in a console app using BenchmarkDotNet, a very powerful benchmarking framework that will help us finely measure performance. Each DbContext will be connecting to its own database on my SQL Server LocalDB instance.

The benchmarks will be run on a .NET Core 3 console app that references both EF6 (6.3.0) and EF Core 3 (3.0.1). Another useful test might be benchmarking on the older .NET framework seeing how there's probably a lot of Entity Framework 6 applications out there running on the full .NET Framework, but unfortuntely Entity Framework Core 3 targets the netstandard2.1, which won't work with the full .NET Framework. Entity Framework Core 3.1, the long term support (LTS) version of Entity Framework Core, will once again target the netstandard2.0. It might be worth revisiting these benchmarks soon.

BenchmarkDotNet - Easily Benchmark Your .NET Code

BenchmarkDotNet has become my defacto decision making tool when it comes to performance testing my .NET code. For scenarios such as Entity Framework 6 vs. Entity Framework Core, I find it to be the perfect tool. To see my previous blog post that served as an introduction to BenchmarkDotNet, click here.

Source Code

If you're interested in the source code I used in benchmarking Entity Framework 6 vs. Entity Framework Core, feel free to visit my Github page for the project here. If you see a mistake on my part, or have a suggestion, feel free to make a pull request!

The Data

The data model will be the same Books SQL Server database I created previously in my post about Entity Framework performance considerations. I seeded both SQL Server databases with similar sets of data as the previous post.

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace EFCore
{
    public class EfCoreDbContext : DbContext
    {
        public DbSet Books { get; set; }
        public DbSet Authors { get; set; }
        public DbSet Copy { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder
                .UseSqlServer(
                    @"Server=(localdb)\msSqlLocalDB; Integrated Security=True; Database=EfCoreBookDB; MultipleActiveResultSets=true;"
                );    
    }

    public class Book
    {
        public int BookId { get; set; }
        public string Name { get; set; }

        public int AuthorId { get; set; }
        public virtual Author Author { get; set; }

        public virtual IList Copies { get; set; }
    }

    public class Author
    {
        public int AuthorId { get; set; }
        public string FullName { get; set; }
    }

    public class Copy
    {
        public int CopyId { get; set; }
        public virtual Book Book { get; set; }
        public decimal Price { get; set; }
    }
}

Comparison #1: Fetching a Single Record

The first comparison is a very common scenario in Entity Framework: fetching a single record from the database. For this test I will use a simple FirstOrDefault() to grab the top 1 Book record from each database.

Code

[Benchmark]
public EF6.Book FirstOrDefaultWithEf6() =>
    _ef6.Books.FirstOrDefault();

[Benchmark]
public EFCore.Book FirstOrDefaultWithEfCore() =>
    _efCore.Books.FirstOrDefault();

Results

Winner: Entity Framework Core 3. It's about 1.73 times faster for retrieving a single record from the database than Entity Framework 6 with FirstOrDefault. Not bad. The measurements other than the mean and medium show the Entity Framework Core version is more steady as well.

MethodMeanErrorStdDevMedian
FirstOrDefaultWithEf6 809.4 us 83.65 us 235.94 us 702.7 us
FirstOrDefaultWithEfCore 466.5 us 19.20 us 54.48 us 467.5 us

us = microseconds

Comparison #2: Fetching Thousands of Records

Another scenario we might see in an Entity Framework application (and a common performance concern at that) is fetching lots of records into memory from the database. In this comparison, we will be fetching all 10,000 books and 1,000 authors from the database into memory. I don't recommend calling .ToList() directly like this in your production code unless you know you're dealing with a very small dataset, as I outline in my blog post about Entity Framework performance concerns, but this will serve as a useful benchmark nonetheless.

Code

public List LoadAllBooksWithAuthors()
{
    return _ef6.Books.Include(book => book.Author).ToList();
}

public List LoadAllBooksWithAuthors()
{
    return _efCore.Books.Include(book => book.Author).ToList();
}

Results

The winner: Entity Framework Core 3. It's about 2.25 times faster than Entity Framework 6 in this scenario, and it's not particularly close. The improvements the Entity Framework Core team have done under the hood are clearly paying off for this scenario. I'm curious to see the general SQL for both frameworks, but I may have to save that for another post.

MethodMeanErrorStdDevMedian
LoadAllBooksWithAuthorsWithEf6183.29 ms3.898 ms3.828 ms181.72 ms
LoadAllBooksWithAuthorsWithEfCore81.31 ms1.510 ms1.438 ms81.46 ms

Comparison #3: Adding, Updating, and Removing a Single Entity

A key feature of Entity Framework is its ability to easily query an entity, create, modify, or delete it, and have the framework do all the heavy lifting in tracking these changes. Let's see how each framework compares.

Code

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using EF6;
using EFCore;
using System;
using System.Linq;

namespace EF.Benchmark
{
    [SimpleJob(RunStrategy.Throughput, launchCount: 1, warmupCount: 5, targetCount: 50)]
    [MedianColumn]
    public class AddRemoveAndDeleteRecordBenchmark
    {
        [Benchmark]
        public void AddWithEf6()
        {
            using var ef6 = new Ef6DbContext();

            var newAuthor = new EF6.Author()
            {
                FullName = "Adding Author with EF6"
            };

            ef6.Authors.Add(newAuthor);

            ef6.SaveChanges();
        }

        [Benchmark]
        public void UpdateWithEf6()
        {
            using var ef6 = new Ef6DbContext();

            var authorToUpdate = ef6.Authors.First();

            authorToUpdate.FullName = DateTime.Now.ToString();

            ef6.SaveChanges();
        }

        [Benchmark]
        public void DeleteWithEf6()
        {
            using var ef6 = new Ef6DbContext();

            var authorToDelete = ef6.Authors
                .Where(author => author.FullName == "Adding Author with EF6")
                .FirstOrDefault();

            ef6.Authors.Remove(authorToDelete);

            ef6.SaveChanges();
        }

        [Benchmark]
        public void AddWithEfCore()
        {
            using var efCore = new EfCoreDbContext();

            var newAuthor = new EFCore.Author()
            {
                FullName = "Adding Author with EF6"
            };

            efCore.Authors.Add(newAuthor);

            efCore.SaveChanges();
        }

        [Benchmark]
        public void UpdateWithEfCore()
        {
            using var efCore = new EfCoreDbContext();

            var authorToUpdate = efCore.Authors.First();

            authorToUpdate.FullName = DateTime.Now.ToString();

            efCore.SaveChanges();
        }

        [Benchmark]
        public void DeleteWithEfCore()
        {
            using var efCore = new EfCoreDbContext();

            var authorToDelete = efCore.Authors
                .Where(author => author.FullName == "Adding Author with EF6")
                .FirstOrDefault();

            efCore.Authors.Remove(authorToDelete);

            efCore.SaveChanges();
        }
    }
}

Results

The winner: Entity Framework Core 3 in all contests. Entity Framework Core 3 is 1.16 times faster, 1.43 times faster, and 1.16 times faster in adding, updating, and deleting single entities, respectively.

MethodMeanErrorStdDevMedian
AddWithEf61,850.6 us157.26 us295.38 us1,746.3 us
UpdateWithEf6949.3 us72.86 us135.05 us899.5 us
DeleteWithEf62,849.7 us194.78 us370.59 us2,768.4 us
AddWithEfCore1,411.7 us76.51 us147.40 us1,391.8 us
UpdateWithEfCore 663.2 us51.18 us96.14 us653.1 us
DeleteWithEfCore2,447.7 us163.57 us319.03 us2,321.5 us

Final Comparison: Adding, Updating, and Deleting Thousands of Entities

The final benchmark of this post involves adding, updating, and deleting 2,500 entities. In my experience, Enity Framework 6 has never been the fastest tool for making bulk changes to a lot of records (though I'm not going to argue Entity Framework 6 is the correct tool for such a task). Let's see how each framework fares.

Code

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using EF6;
using EFCore;
using System;
using System.Collections.Generic;
using System.Linq;

namespace EF.Benchmark
{
    [SimpleJob(RunStrategy.Throughput, launchCount: 1, warmupCount: 5, targetCount: 20)]
    [MedianColumn]
    public class AddRemoveDeleteNAuthors
    {
        const int N = 2500;

        [Benchmark]
        public void AddNAuthorsToEf6()
        {
            using var ef6 = new Ef6DbContext();

            var authors = new List();
            for (int i = 0; i < N; i++)
            {
                authors.Add(new EF6.Author()
                {
                    FullName = "New Author " + i
                });
            }

            ef6.Authors.AddRange(authors);

            ef6.SaveChanges();
        }

        [Benchmark]
        public void LoadAndUpdateNAuthorsWithEf6()
        {
            using var ef6 = new Ef6DbContext();

            foreach (var author in ef6.Authors.Take(N))
            {
                author.FullName = DateTime.Now.ToString();
            }

            ef6.SaveChanges();
        }

        [Benchmark]
        public void LoadAndDeleteNAuthorsWithEf6()
        {
            using var ef6 = new Ef6DbContext();

            var authors = ef6
                .Authors
                .Where(author => author.FullName.StartsWith("New Author"))
                .Take(N)
                .ToList();

            ef6.Authors.RemoveRange(authors);
            ef6.SaveChanges();
        }

        [Benchmark]
        public void AddNAuthorToEfCore()
        {
            using var efCore = new EfCoreDbContext();

            var authors = new List();
            for (int i = 0; i < N; i++)
            {
                authors.Add(new EFCore.Author()
                {
                    FullName = "New Author " + i
                });
            }

            efCore.AddRange(authors);

            efCore.SaveChanges();
        }

        [Benchmark]
        public void LoadAndUpdateNAuthorsWithEfCore()
        {
            using var efCore = new EfCoreDbContext();

            foreach (var author in efCore.Authors.Take(N))
            {
                author.FullName = DateTime.Now.ToString();
            }

            efCore.SaveChanges();
        }

        [Benchmark]
        public void LoadAndDeleteNAuthorsWithEfCore()
        {
            using var efCore = new EfCoreDbContext();

            var authors = efCore
                .Authors
                .Where(author => author.FullName.StartsWith("New Author"))
                .Take(N)
                .ToList();

            efCore.Authors.RemoveRange(authors);
            efCore.SaveChanges();
        }
    }
}

Results

The winner: Entity Framework Core 3 for adding and deleting entities. Entity Framework 6 for updating entities. Curiously enough, Entity Framework 6 outperforms the newer Entity Framework Core 3 by a noticeable amount. Entity Framework Core 3 is 4.15 times faster for adds and 2.25 times faster for deletes.

This is really where it seems like Entity Framework Core 3 shines. I'm a bit surprise though that the updates were faster with Entity Framework 6. I may need to do a deeper dive and see what's going on under the hood.

MethodMeanErrorStdDevMedian
AddWithEf6494.51 ms6.987 ms 7.766 ms493.43 ms
UpdateWithEf610.17 ms0.363 ms0.403 ms10.07 ms
DeleteWithEf6476.36 ms51.645 ms50.722 ms489.42 ms
AddWithEfCore119.28 ms2.577 ms2.757 ms118.28 ms
UpdateWithEfCore26.63 ms6.645 ms7.111 ms25.55 ms
DeleteWithEfCore211.02 ms18.064 ms20.802 ms211.78 ms

Closing Remarks

For most common scenarios, as we've tested, Entity Framework Core 3 is the clear winner for performance. I highly recommend benchmarking your own .NET code to figure out how much you stand to gain from using Entity Framework Core 3 over Entity Framework 6. These benchmarks are hardly indicitive of any production scenario, and you may see either smaller or larger gains depending on how elaborate your data model is, or what your network infrastructure looks like. I'd wager that if Entity Framework Core 3 is performing less roundtrips to your database, then the performance gains I've demonstrated may be modest compared to what's really possible.

My console app and my SQL Server database exist on the same machine, so it might not be particularly obvious if less roundtrips are responsible for much of the performance gains. I'd recommend running benchmarks like these (or any Entity Framework code for that matter!) with a tool like SQL Server profiler to see what kind of SQL your Entity Framework Code is generating for you.

I can't reiterate enough to run your own benchmarks to suit your own situation to find out which is better for your needs. Based on the results of my own testing, if performance is high on my priority list, I most certainly would begin new projects using Entity Framework Core 3, and I'd even consider upgrading existing Entity Framework 6 applications if the performance gains are enough to offset the cost of upgrading your project.

Happy coding!

Read Next

Multi-Tenanted Entity Framework Core Migration Deployment image

April 11, 2021 by Chad

Multi-Tenanted Entity Framework Core Migration Deployment

There's many ways to deploy pending Entity Framework Core (EF Core) migrations, especially for multi-tenanted scenarios. In this post, I'll demonstrate a strategy to efficiently apply pending EF Core 6 migrations using a .NET 6 console app.

Read Article
Multi-Tenanted Entity Framework 6 Migration Deployment image

April 10, 2021 by Chad

Multi-Tenanted Entity Framework 6 Migration Deployment

There's many ways to deploy pending Entity Framework 6 (EF6) migrations, especially for multi-tenanted production scenarios. In this post, I'll demonstrate a strategy to efficiently apply pending migrations using a .NET 6 console app.

Read Article
Entity Framework Performance: 3 Things You Must Consider image

July 20, 2019 by Chad

Entity Framework Performance: 3 Things You Must Consider

I hear it all the time: Entity Framework is slow, Entity Framework can't handle this kind of volume, We need to rip out Entity Framework for regular SQL. In some cases this is necessary, but let me demonstrate a few easy ways to make sure you're eeking the most performance out of your Entity Framework queries.

Read Article