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.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
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.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
LoadAllBooksWithAuthorsWithEf6 | 183.29 ms | 3.898 ms | 3.828 ms | 181.72 ms |
LoadAllBooksWithAuthorsWithEfCore | 81.31 ms | 1.510 ms | 1.438 ms | 81.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.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
AddWithEf6 | 1,850.6 us | 157.26 us | 295.38 us | 1,746.3 us |
UpdateWithEf6 | 949.3 us | 72.86 us | 135.05 us | 899.5 us |
DeleteWithEf6 | 2,849.7 us | 194.78 us | 370.59 us | 2,768.4 us |
AddWithEfCore | 1,411.7 us | 76.51 us | 147.40 us | 1,391.8 us |
UpdateWithEfCore | 663.2 us | 51.18 us | 96.14 us | 653.1 us |
DeleteWithEfCore | 2,447.7 us | 163.57 us | 319.03 us | 2,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.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
AddWithEf6 | 494.51 ms | 6.987 ms | 7.766 ms | 493.43 ms |
UpdateWithEf6 | 10.17 ms | 0.363 ms | 0.403 ms | 10.07 ms |
DeleteWithEf6 | 476.36 ms | 51.645 ms | 50.722 ms | 489.42 ms |
AddWithEfCore | 119.28 ms | 2.577 ms | 2.757 ms | 118.28 ms |
UpdateWithEfCore | 26.63 ms | 6.645 ms | 7.111 ms | 25.55 ms |
DeleteWithEfCore | 211.02 ms | 18.064 ms | 20.802 ms | 211.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!