Boosting .NET App Performance with AsNoTracking

Hi, developers!

Today, let’s talk about how we can improve performance in .NET applications via the AsNoTracking method from Entity Framework.
Requests from DB can be tracked or untracked. By default, all queries that return model objects from the DB are trackable (unless you changed the default behaviour with AutoDetectChangesEnabled property, we will talk about in a minute).

When the data context retrieves data from the database, the Entity Framework places the retrieved objects in the cache and monitors changes that occur to those objects until it uses the SaveChanges() or SaveChangesAsync() method, which commits any changes to the database. But we don’t always need to track changes. For example, we just need to display data for viewing.

To prevent the data from being cached, the AsNoTracking() method is used. When AsNoTracking() is applied, the data returned from the request is not cached; that is, the request becomes untracked. This means that the Entity Framework does not perform any additional processing and does not allocate additional space for storing objects retаrieved from the database. And therefore, such queries are faster.

Now let’s get down to practice 🙂

Let’s say we have the following models and data context:

using Microsoft.EntityFrameworkCore;

namespace AsNoTrackinsExample
{
    public class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }

    public class AppDbContext : DbContext
    {
        public DbSet Users { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("yoursConnectionString");
        }
    }
}

And I’ve inserted in the database 6828 rows for Users the table.

Image 23 - Improve performance in your .NET app via one simple line of code

Next step — to get data from the DB without AsNoTracking and with AsNoTracking and compare performance. For comparison, I use Benchmark.

using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using Microsoft.EntityFrameworkCore;

namespace AsNoTrackinsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run();
        }
    }

    [MemoryDiagnoser]
    public class MemoryBenchmarkerDemo
    {
        [Benchmark]
        public List GetUsers()
        {
            using (AppDbContext db = new AppDbContext())
            {
                return db.Users.ToList();
            }
        }

        [Benchmark]
        public List GetUsersWithNoTracking()
        {
            using (AppDbContext db = new AppDbContext())
            {
                return db.Users.AsNoTracking().ToList();
            }
        }
    }
}

Image 24 - Improve performance in your .NET app via one simple line of code

Wow! We can see an advantage in both speed of execution and memory usage. Mostly, the differences will be noticeable on large amounts of data.

But remember that we can not update any item or collection that we received with AsNoTracking, because Entity Framework does not store these objects in the cache.

using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace AsNoTrackinsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (AppDbContext db = new AppDbContext())
            {
                var user = db.Users.AsNoTracking().FirstOrDefault();
                user.Age = 22;
                db.SaveChanges(); // Age will not be updated 
            }
        }
    }
}

In addition to using the AsNoTracking method, you can disable tracking in general for the context object.

To do this, set the false value for the db.ChangeTracker.AutoDetectChangesEnabled property:

using Microsoft.EntityFrameworkCore;

namespace AsNoTrackinsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (AppDbContext db = new AppDbContext())
            {
                db.ChangeTracker.AutoDetectChangesEnabled = false;
                ...
            }
        }
    }
}

In general, through the ChangeTracker property, we can manage the tracking of an object and receive a variety of information. For example, we can find out how many objects are currently being tracked:

using System.Linq;

namespace AsNoTrackinsExample
{
    class Program
    {
        static void Main(string[] args)
        {
            using (AppDbContext db = new AppDbContext())
            {
                var users = db.Users.ToList();

                int count = db.ChangeTracker.Entries().Count();
            }
        }
    }
}

When should you use AsNoTracking? If you just need to display data for display without the need for further updating, then this is the case when we can use AsNoTracking.

Happy coding