1
Entity Framework Tracking v No Tracking queries with EF 8 on .NET 8
Date Added (UTC):
07 Apr 2024 @ 20:59
Date Updated (UTC):12 Apr 2024 @ 02:02
.NET Version(s): Tag(s):
Added By:
.NET Developer and tech lead from Ireland!
Benchmark Results:
Benchmark Code:
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using Microsoft.EntityFrameworkCore;
namespace Benchmarks;
[MemoryDiagnoser]
public class QueryTrackingBehavior
{
[Params(10)]
public int NumBlogs { get; set; }
[Params(20)]
public int NumPostsPerBlog { get; set; }
[GlobalSetup]
public void Setup()
{
Console.WriteLine("Setting up database...");
using var context = new BloggingContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
BloggingContext.SeedData(NumBlogs, NumPostsPerBlog);
Console.WriteLine("Setup complete.");
}
[Benchmark(Baseline = true)]
public List<Post> AsTracking()
{
using var context = new BloggingContext();
return context.Posts.AsTracking().Include(p => p.Blog).ToList();
}
[Benchmark]
public List<Post> AsNoTracking()
{
using var context = new BloggingContext();
return context.Posts.AsNoTracking().Include(p => p.Blog).ToList();
}
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(@"Server=localhost;Database=Blogging;Trusted_Connection=True;TrustServerCertificate=true");
public static void SeedData(int numBlogs, int numPostsPerBlog)
{
using var context = new BloggingContext();
context.AddRange(
Enumerable.Range(0, numBlogs)
.Select(_ => new Blog { Url = "Some URL", Posts = Enumerable.Range(0, numPostsPerBlog)
.Select(_ => new Post() { Title = "Some Title", Content = "Some Content"}).ToList() }));
context.SaveChanges();
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}
Powered by SharpLab
// .NET 8 Lowered C# Code unavailable due to errors:
error CS0234: The type or namespace name 'EntityFrameworkCore' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'DbContext' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DbSet<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DbContextOptionsBuilder' could not be found (are you missing a using directive or an assembly reference?)
error CS1674: 'QueryTrackingBehavior.BloggingContext': type used in a using statement must be implicitly convertible to 'System.IDisposable'.
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'Database' and no accessible extension method 'Database' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'AddRange' and no accessible extension method 'AddRange' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'SaveChanges' and no accessible extension method 'SaveChanges' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
Powered by SharpLab
// .NET 8 IL Code unavailable due to errors:
error CS0234: The type or namespace name 'EntityFrameworkCore' does not exist in the namespace 'Microsoft' (are you missing an assembly reference?)
error CS0246: The type or namespace name 'DbContext' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DbSet<>' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'DbContextOptionsBuilder' could not be found (are you missing a using directive or an assembly reference?)
error CS1674: 'QueryTrackingBehavior.BloggingContext': type used in a using statement must be implicitly convertible to 'System.IDisposable'.
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'Database' and no accessible extension method 'Database' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'AddRange' and no accessible extension method 'AddRange' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
error CS1061: 'QueryTrackingBehavior.BloggingContext' does not contain a definition for 'SaveChanges' and no accessible extension method 'SaveChanges' accepting a first argument of type 'QueryTrackingBehavior.BloggingContext' could be found (are you missing a using directive or an assembly reference?)
Powered by SharpLab
|
Benchmark Description:
Using AsNoTracking on read only queries is one of the simplest ways to improve performance. It can make a big difference particularly when we are pulling back a lot of records for large entities.
This benchmark setup is designed to measure and compare the performance of two different query tracking behaviors in Entity Framework Core (EF Core): tracking (`AsTracking()`) and no-tracking (`AsNoTracking()`) queries. The setup involves a simulated blogging platform database context with a specific number of blogs and posts per blog. The `.NET` version isn't explicitly mentioned, but given the use of `EntityFrameworkCore`, it's likely targeting `.NET Core 3.1` or later, as EF Core is a modern ORM (Object-Relational Mapping) tool designed for such frameworks.
### General Setup
- **Database Configuration**: The benchmark uses a SQL Server database configured in the `OnConfiguring` method of the `BloggingContext` class. It assumes a local SQL Server instance with a database named `Blogging`.
- **Data Seeding**: In the `GlobalSetup` method, the database is reset (deleted and recreated) for each benchmark run, and then seeded with a predetermined number of blogs and posts per blog. This ensures that each benchmark run starts with an identical dataset, making the results comparable.
- **Benchmark Configuration**: The benchmark class is decorated with `[MemoryDiagnoser]`, indicating that memory allocation is being measured alongside execution time. Parameters for the number of blogs and posts per blog are set using `[Params]` attributes, allowing for easy adjustment of the dataset size.
### Benchmark Methods
#### AsTracking Method
- **Purpose**: This method measures the performance of executing a query that tracks changes to the returned entities. In EF Core, tracking queries keep track of changes made to the entity instances so that these changes can be persisted to the database during `SaveChanges()`.
- **Performance Aspect**: The benchmark assesses how quickly EF Core can execute a tracking query and map the results to entity instances, including the overhead of setting up change tracking.
- **Expectations**: Tracking queries generally consume more memory and take longer to execute compared to no-tracking queries, as EF Core needs to set up the infrastructure for tracking changes to the entities. This method serves as the baseline for comparison.
#### AsNoTracking Method
- **Purpose**: This method evaluates the performance of executing a query that does not track changes to the entities. No-tracking queries are useful for read-only scenarios where the results are used for display or processing without the need to update the data in the database.
- **Performance Aspect**: The benchmark focuses on the execution speed and memory efficiency of no-tracking queries, highlighting the potential performance benefits for read-only scenarios.
- **Expectations**: No-tracking queries are expected to be faster and use less memory than tracking queries, as EF Core does not need to set up change tracking. This can lead to significant performance improvements, especially in scenarios where the tracked entities are not modified.
### Insights
From running these benchmarks, you should expect to gain insights into the trade-offs between using tracking and no-tracking queries in EF Core. Specifically, you'll be able to quantify the performance and memory allocation differences between these two approaches, which can inform optimization decisions in applications that make heavy use of EF Core for data access. Understanding these trade-offs is crucial for optimizing application performance, especially in scenarios with large datasets or high throughput requirements.