Entity Framework
DbContext
Discard Changes
.NET
Data Management

DbContext discard changes without disposing

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Entity Framework, DbContext tracks changes to entities until you call SaveChanges(). If you want to throw those changes away without disposing the context, you need to reset or detach the tracked entries so the change tracker no longer treats them as pending work.

Understand what needs to be discarded

DbContext does not store one vague "dirty" flag. It tracks each entity with a state such as:

  • 'Added'
  • 'Modified'
  • 'Deleted'
  • 'Unchanged'
  • 'Detached'

Discarding changes means moving entries out of the pending states and back to a clean state. The correct action depends on the current state of each entry.

Reset tracked entities manually

A common pattern is:

  • detach added entities
  • restore modified entities to Unchanged
  • reload deleted or modified entities from the database when necessary
csharp
1using Microsoft.EntityFrameworkCore;
2
3public static void DiscardChanges(DbContext context)
4{
5    foreach (var entry in context.ChangeTracker.Entries().ToList())
6    {
7        switch (entry.State)
8        {
9            case EntityState.Added:
10                entry.State = EntityState.Detached;
11                break;
12
13            case EntityState.Modified:
14                entry.CurrentValues.SetValues(entry.OriginalValues);
15                entry.State = EntityState.Unchanged;
16                break;
17
18            case EntityState.Deleted:
19                entry.State = EntityState.Unchanged;
20                break;
21        }
22    }
23}

This works for many cases, especially when the original values are still available in the change tracker.

Reload from the database when accuracy matters

If the entity may have changed in the database since it was loaded, Reload() is safer than simply copying original values back.

csharp
1using Microsoft.EntityFrameworkCore;
2
3public static async Task ReloadTrackedEntitiesAsync(DbContext context)
4{
5    foreach (var entry in context.ChangeTracker.Entries().ToList())
6    {
7        if (entry.State == EntityState.Added)
8        {
9            entry.State = EntityState.Detached;
10        }
11        else if (entry.State == EntityState.Modified || entry.State == EntityState.Deleted)
12        {
13            await entry.ReloadAsync();
14        }
15    }
16}

ReloadAsync() asks the database for the current persisted version and resets the tracked state accordingly.

That is often the best choice when the context has been alive long enough that the database may no longer match the originally loaded snapshot.

Use ChangeTracker.Clear() when detaching everything is acceptable

In newer Entity Framework Core versions, ChangeTracker.Clear() detaches all tracked entities in one call.

csharp
context.ChangeTracker.Clear();

This is simple, but it is not the same as "revert the objects and keep tracking them." It removes the tracked entities from the context entirely. That may be exactly what you want, but it changes how later code interacts with those objects.

Use it when:

  • the context should keep living
  • you do not need the currently tracked graph anymore
  • requerying later is acceptable

Why disposing is still often the better design

A long-lived DbContext is usually a design smell. The simplest way to discard changes is often to use a short-lived context and dispose it.

csharp
1await using var context = new AppDbContext(options);
2
3var customer = await context.Customers.FindAsync(1);
4customer.Name = "Temporary change";
5
6// do not call SaveChanges()

Once the context is disposed, the pending changes disappear naturally. This is one reason EF guidance generally favors short-lived contexts per unit of work.

If you often need "discard changes without disposing," it is worth checking whether the context lifetime is longer than it should be.

Common Pitfalls

The biggest mistake is setting every entry to Unchanged blindly. That leaves added entities tracked even though they do not exist in the database, which can create confusing behavior later.

Another issue is assuming local original values always match the database. If other processes may have updated the same row, Reload() is safer than just restoring OriginalValues.

Developers also use ChangeTracker.Clear() without realizing it detaches everything. That is convenient, but it means later code will not see automatic change tracking on those entity instances anymore.

Finally, if discarding changes is a regular requirement, review the lifetime of the DbContext. Many of these problems become simpler when the context is scoped tightly to one unit of work.

Summary

  • Discarding changes means resetting or detaching tracked entries, not toggling one global flag.
  • Added, modified, and deleted entities often need different reset behavior.
  • 'Reload() is useful when you want to restore from the current database state.'
  • 'ChangeTracker.Clear() detaches everything and is simple when full reset is acceptable.'
  • Short-lived DbContext instances are usually a cleaner design than frequent manual rollback.

Course illustration
Course illustration

All Rights Reserved.