Disable Entity Tracking



Disable tracking changes for a specific entity

  • You can prevent the session from persisting changes made to a specific entity by using IgnoreChangesFor.
  • Once changes are ignored for the entity:
    • Any modifications made to the entity will be ignored by SaveChanges.
    • The session will still keep a reference to the entity to avoid repeated server requests.
      Performing another Load for the same entity will Not generate another call to the server.

Example

// Load a product entity - the session will track the entity by default
Product product = session.Load<Product>("products/1-A");

// Call 'IgnoreChangesFor' to instruct the session to ignore changes made to this entity
session.Advanced.IgnoreChangesFor(product);

// The following change will be ignored by SaveChanges - it will not be persisted
product.UnitsInStock += 1;

session.SaveChanges();
// Load a product entity - the session will track the entity by default
Product product = await asyncSession.LoadAsync<Product>("products/1-A");

// Call 'IgnoreChangesFor' to instruct the session to ignore changes made to this entity
asyncSession.Advanced.IgnoreChangesFor(product);

// The following change will be ignored by SaveChanges - it will not be persisted
product.UnitsInStock += 1;

await asyncSession.SaveChangesAsync();

Syntax

void IgnoreChangesFor(object entity);
Parameter Type Description
entity object Instance of entity for which changes will be ignored

Disable tracking all entities in session

  • Tracking can be disabled for all entities in the session's options.
  • When tracking is disabled for the session:
    • Method Store will Not be available (an exception will be thrown if used).
    • Calling Load or Query will generate a call to the server and create new entities instances.

using (IDocumentSession session = store.OpenSession(new SessionOptions
{
    // Disable tracking for all entities in the session's options
    NoTracking = true
}))
{
    // Load any entity, it will Not be tracked by the session
    Employee employee1 = session.Load<Employee>("employees/1-A");
    
    // Loading again from same document will result in a new entity instance
    Employee employee2 = session.Load<Employee>("employees/1-A");
    
    // Entities instances are not the same
    Assert.NotEqual(employee1, employee2);
}
using (IAsyncDocumentSession asyncSession = store.OpenAsyncSession(new SessionOptions
{
    // Disable tracking for all entities in the session's options
    NoTracking = true
}))
{
    // Load any entity, it will Not be tracked by the session
    Employee employee1 = await asyncSession.LoadAsync<Employee>("employees/1-A");
    
    // Loading again from same document will result in a new entity instance
    Employee employee2 = await asyncSession.LoadAsync<Employee>("employees/1-A");

    // Entities instances are not the same
    Assert.NotEqual(employee1, employee2);
}

Disable tracking query results

  • Tracking can be disabled for all entities resulting from a query.

using (IDocumentSession session = store.OpenSession())
{
    // Define a query
    List<Employee> employeesResults = session.Query<Employee>()
        // Set NoTracking, all resulting entities will not be tracked
        .Customize(x => x.NoTracking())
        .Where(x => x.FirstName == "Robert")
        .ToList();

    // The following modification will not be tracked for SaveChanges
    Employee firstEmployee = employeesResults[0];
    firstEmployee.LastName = "NewName";
    
    // Change to 'firstEmployee' will not be persisted
    session.SaveChanges();
}
using (IAsyncDocumentSession asyncSession = store.OpenAsyncSession())
{
    // Define a query
    List<Employee> employeesResults = asyncSession.Query<Employee>()
        // Set NoTracking, all resulting entities will not be tracked
        .Customize(x => x.NoTracking())
        .Where(x => x.FirstName == "Robert")
        .ToList();

    // The following modification will not be tracked for SaveChanges
    Employee firstEmployee = employeesResults[0];
    firstEmployee.LastName = "NewName";
    
    // Change to 'firstEmployee' will not be persisted
    await asyncSession.SaveChangesAsync();
}
using (IDocumentSession session = store.OpenSession())
{
    // Define a query
    List<Employee> employeesResults = session.Advanced.DocumentQuery<Employee>()
        // Set NoTracking, all resulting entities will not be tracked
        .NoTracking()
        .Where(x => x.FirstName == "Robert")
        .ToList();

    // The following modification will not be tracked for SaveChanges
    Employee firstEmployee = employeesResults[0];
    firstEmployee.LastName = "NewName";
    
    // Change to 'firstEmployee' will not be persisted
    session.SaveChanges();
}
using (IAsyncDocumentSession asyncSession = store.OpenAsyncSession())
{
    // Define a query
    List<Employee> employeesResults = asyncSession.Advanced.AsyncDocumentQuery<Employee>()
        // Set NoTracking, all resulting entities will not be tracked
        .NoTracking()
        .Where(x => x.FirstName == "Robert")
        .ToList();

    // The following modification will not be tracked for SaveChanges
    Employee firstEmployee = employeesResults[0];
    firstEmployee.LastName = "NewName";
    
    // Change to 'firstEmployee' will not be persisted
    await asyncSession.SaveChangesAsync();
}

Customize tracking in conventions

  • You can further customize and fine-tune which entities will not be tracked
    by configuring the ShouldIgnoreEntityChanges convention method on the document store.
  • This customization rule will apply to all sessions opened for this document store.

Example

using (var store = new DocumentStore()
{
    // Define the 'ignore' convention on your document store
    Conventions =
    {
        ShouldIgnoreEntityChanges =
            // Define for which entities tracking should be disabled 
            // Tracking will be disabled ONLY for entities of type Employee whose FirstName is Bob
            (session, entity, id) => (entity is Employee e) &&
                                     (e.FirstName == "Bob")
    }
}.Initialize())
{
    using (IDocumentSession session = store.OpenSession())
    {
        var employee1 = new Employee { Id = "employees/1", FirstName = "Alice" };
        var employee2 = new Employee { Id = "employees/2", FirstName = "Bob" };

        session.Store(employee1);      // This entity will be tracked
        session.Store(employee2);      // Changes to this entity will be ignored

        session.SaveChanges();         // Only employee1 will be persisted

        employee1.FirstName = "Bob";   // Changes to this entity will now be ignored
        employee2.FirstName = "Alice"; // This entity will now be tracked

        session.SaveChanges();         // Only employee2 is persisted
    }
}

// todo .. async...

Syntax

public Func<InMemoryDocumentSessionOperations, object, string, bool> ShouldIgnoreEntityChanges;
Parameter Description
InMemoryDocumentSessionOperations The session for which tracking is to be disabled
object The entity for which tracking is to be disabled
string The entity's document ID
Return Type Description
bool true - Entity will Not be tracked
false - Entity will be tracked

Using 'Include' in a NoTracking session will throw

  • Attempting to use Include in a NoTracking session will throw an exception.

  • Like other entities in a NoTracking session, the included items are not tracked and will not prevent additional server requests during subsequent Load operations for the same data. To avoid confusion, Include operations are disallowed during non-tracking session actions such as Load or Query.

  • This applies to all items that can be included -
    e.g., documents, compare-exchange items, counters, revisions, and time series.


Include when loading:

using (IDocumentSession session = store.OpenSession(new SessionOptions
{
    // Working with a non-tracking session
    NoTracking = true
}))
{
    try
    {
        // Trying to include a related document when loading a document will throw:
        Product product1 = session
            .Include<Product>(x => x.Supplier)
            .Load<Product>("products/1-A");
        
        // The same applies when using the builder syntax:
        Product product2 = session.Load<Product>("products/1-A",
            builder => builder.IncludeDocuments(product => product.Supplier));
    }
    catch (InvalidOperationException e)
    {
        // An InvalidOperationException is expected here
    }
}
using (IAsyncDocumentSession asyncSession = store.OpenAsyncSession(new SessionOptions
       {
           // Working with a non-tracking session
           NoTracking = true
       }))
{
    try
    {
        // Trying to include a related document when loading a document will throw:
        Product product = await asyncSession
            .Include<Product>(x => x.Supplier)
            .LoadAsync<Product>("products/1-A");
        
        // The same applies when using the builder syntax:
        Product product2 = await asyncSession.LoadAsync<Product>("products/1-A",
            builder => builder.IncludeDocuments(product => product.Supplier));
    }
    catch (InvalidOperationException e)
    {
        // An InvalidOperationException is expected here
    }
}

Include when querying:

using (IDocumentSession session = store.OpenSession(new SessionOptions
       {
           // Working with a non-tracking session
           NoTracking = true
       }))
{
    try
    {
        // Trying to include related documents in a query will throw
        var products = session
            .Query<Product>()
            .Include(x => x.Supplier)
            .ToList();
    }
    catch (InvalidOperationException e)
    {
        // An InvalidOperationException is expected here
    }
}
using (IAsyncDocumentSession asyncSession = store.OpenAsyncSession(new SessionOptions
       {
           // Working with a non-tracking session
           NoTracking = true
       }))
{
    try
    {
        // Trying to include related documents when making a query will throw
        var products = await asyncSession
            .Query<Product>()
            .Include(x => x.Supplier)
            .ToListAsync();
    }
    catch (InvalidOperationException e)
    {
        // An InvalidOperationException is expected here
    }
}