3


EF Core 8.0 Tracking vs AsNoTracking vs AsNoTrackingWithIdentityResolution




Date Added (UTC):

02 May 2024 @ 16:53

Date Updated (UTC):

02 May 2024 @ 17:45


.NET Version(s):

.NET 8

Tag(s):

#EntityFramework #ObjectMapping


Added By:
Profile Image

Blog   
Leeds, United Kingdom    
Microsoft MVP | A Developer

Benchmark Results:





Benchmark Code:



using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Reports;
using EFQueryOptimization.Context;
using EFQueryOptimization.Entities;
using Microsoft.EntityFrameworkCore;

namespace EFQueryOptimization;

[Config(typeof(Config))]
[HideColumns(Column.RatioSD, Column.AllocRatio)]
[MemoryDiagnoser(false)]
public class EFNoTrackingBenchmark
{
    private class Config : ManualConfig
    {
        public Config()
        {
            SummaryStyle = SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend);
        }
    }

    [Benchmark(Baseline = true)]
    public List<Role> Tracking()
    {
        var context = new ApplicationDbContext();

        return context.Roles
            .Include(x => x.UserRoles)
            .ThenInclude(x => x.User)
            .Take(10)
            .ToList();
    }

    [Benchmark]
    public List<Role> NoTracking()
    {
        var context = new ApplicationDbContext();

        return context.Roles
            .Include(x => x.UserRoles)
            .ThenInclude(x => x.User)
            .AsNoTracking()
            .Take(10)
            .ToList();
    }

    [Benchmark]
    public List<Role> NoTrackingWithIdentityResolution()
    {
        var context = new ApplicationDbContext();

        return context.Roles
            .Include(x => x.UserRoles)
            .ThenInclude(x => x.User)
            .AsNoTrackingWithIdentityResolution()
            .Take(10)
            .ToList();
    }

}

// .NET 8 Lowered C# Code unavailable due to errors:
error CS0234: The type or namespace name 'Context' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Entities' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
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 'Role' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ApplicationDbContext' could not be found (are you missing a using directive or an assembly reference?)

// .NET 8 IL Code unavailable due to errors:
error CS0234: The type or namespace name 'Context' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Entities' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
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 'Role' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ApplicationDbContext' could not be found (are you missing a using directive or an assembly reference?)

// .NET 8 Jit Asm Code unavailable due to errors:
error CS0234: The type or namespace name 'Context' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
error CS0234: The type or namespace name 'Entities' does not exist in the namespace 'EFQueryOptimization' (are you missing an assembly reference?)
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 'Role' could not be found (are you missing a using directive or an assembly reference?)
error CS0246: The type or namespace name 'ApplicationDbContext' could not be found (are you missing a using directive or an assembly reference?)


Benchmark Description:


Here is my setup: **EF Core: 8.0.3** **Users: *15,000*** **Roles: *50*** **UserRoles: *22,592*** Using **AsNoTracking** has the best performance in the case of both memory allocation and query execution time. BUT seems **AsNoTrackingWithIdentityResolution** is not performant at all there is an open issue in the EF core about this problem: [https://github.com/dotnet/efcore/issues/23558](https://github.com/dotnet/efcore/issues/23558)

The provided benchmark code is designed to measure and compare the performance of different Entity Framework (EF) Core query optimization techniques, specifically focusing on the impact of tracking vs. no-tracking queries and the effect of identity resolution on no-tracking queries. This benchmark setup uses BenchmarkDotNet, a powerful .NET library for benchmarking, to provide accurate and detailed performance metrics. The .NET version isn't explicitly mentioned, but it's important to use a version that supports the features being tested, likely .NET Core 3.1 or .NET 5/6, given the use of `AsNoTrackingWithIdentityResolution()` which was introduced in EF Core 5.0. ### General Setup - **BenchmarkDotNet Configuration**: The benchmark class is annotated with `[Config(typeof(Config))]`, which specifies a custom configuration for the benchmark runs. This configuration adjusts the summary style and hides certain columns (Ratio Standard Deviation and Allocation Ratio) in the output to focus on the most relevant metrics. - **Memory Diagnoser**: The `[MemoryDiagnoser(false)]` attribute indicates that memory diagnostics are enabled for the benchmarks, but GC (Garbage Collection) statistics will not be included in the output. This helps in understanding the memory allocation behavior of each query type without the overhead of detailed GC metrics. - **EF Core Setup**: The benchmarks are performed using an `ApplicationDbContext`, which is assumed to be an Entity Framework Core DbContext configured to connect to a database containing `Roles` and related entities (`UserRoles` and `Users`). ### Benchmark Methods #### 1. Tracking() - **Purpose**: This method serves as the baseline for comparison. It executes a query to fetch the first 10 `Role` entities from the database, including related `UserRoles` and `Users`, with change tracking enabled. - **Performance Aspect**: It measures the time and resources required to execute a tracked query, which EF Core uses to monitor changes to the returned entities for potential updates back to the database. - **Insights**: Results from this benchmark provide a baseline for understanding the overhead associated with change tracking in EF Core queries. #### 2. NoTracking() - **Purpose**: This method tests the performance of executing the same query as the `Tracking()` method but with change tracking disabled using `.AsNoTracking()`. - **Performance Aspect**: It focuses on the potential performance improvement by avoiding the overhead of setting up change tracking for the entities returned by the query. - **Insights**: Results will show how much overhead is saved by disabling change tracking, which is particularly useful for read-only scenarios where updates to the entities are not required. #### 3. NoTrackingWithIdentityResolution() - **Purpose**: This method evaluates the performance impact of disabling change tracking while still enabling identity resolution using `.AsNoTrackingWithIdentityResolution()`. Identity resolution ensures that the same entity instance is returned for each occurrence in the result set, which can be important for data consistency in the results. - **Performance Aspect**: It measures the trade-off between the performance benefits of no tracking and the potential overhead of identity resolution. - **Insights**: The results will indicate whether the identity resolution feature in no-tracking queries introduces significant overhead compared to plain no-tracking queries and how it compares to fully tracked queries. ### Expected Results - **Tracking vs. NoTracking**: You should expect the `NoTracking()` method to be faster and allocate less memory than the `Tracking()` method, as it avoids the overhead of setting up change tracking. - **NoTracking vs. NoTrackingWithIdentityResolution**: The comparison between `NoTracking()` and `NoTrackingWithIdentityResolution()` will show whether the identity resolution feature adds significant overhead. The `NoTrackingWithIdentityResolution()` might be slightly slower than `NoTracking()` due to the additional work to ensure identity resolution but should still be faster than fully tracked queries. These benchmarks are important for understanding the performance characteristics of EF Core queries in different scenarios, helping developers make informed decisions about query optimization techniques based on their specific requirements.


Benchmark Comments: