Home > Software engineering >  Is it always necessary to call DetectChanges with DBSet.AddRangeAsync?
Is it always necessary to call DetectChanges with DBSet.AddRangeAsync?

Time:01-24

I want to add some registers using EF into the DB the fastest it can. At the moment I am doing this:

private async Task AddItems()
{
    List<ItemRecipe> list = new List<ItemRecipe>() { new ItemRecipe(), new ItemRecipe() };
    
    // The Context is a DBContext, ItemRecipes is a DBSet
    await Context.ItemRecipes.AddRangeAsync(list);
}

Is it always necessary that DBSet.AddRangeAsync(..) calls the DetectChanges() internally? Can I disable it (in this specific case) with:

Context.ChangeTracker.AutoDetectChangesEnabled = false;

I'd like to avoid calling DetectChanges() as many times as possible, as i track many entitities.

Would it be the same answer, if the ItemRecipe had a FK to another entity? For example:

ItemRecipe item = new();
item.Recipe = new Recipe(); // This entity is new and needs to be inserted also in the DB (Foreign Key).
item.Recipe.Formula = new Formula(); // Idem

And if the"Recipe" already exists in the DB and it's already tracked?

CodePudding user response:

Is it always necessary that DBSet.AddRangeAsync(..) calls the DetectChanges() internally?

While AddRange(Async) already is far more efficient than Add(Async) because the latter calls DetectChanges for each added item, no, it it is not necessary for EF to detect changes after the call. EF will always detect changes before saving changes.

Turning off AutoDetectChangesEnabled can boost performance considerably as I know by experience. But you have to be aware of possible consequences.

Unfortunately, it's hard to find the exact details on this. The documentation I could find is fragmentary and maybe not up to date. For sure, the main consequence of turning off change detection is that relationship fixup may not take place completely. But, for example, my experience differs from what's said here:

Starting with EF Core 3.0, calling DbContext.Entry will now only attempt to detect changes in the given entity and any tracked principal entities related to it. This means that changes elsewhere may not have been detected by calling this method, which could have implications on application state.

Note that if ChangeTracker.AutoDetectChangesEnabled is set to false then even this local change detection will be disabled.

In EFC 7 I still see some local change tracking occurring when change detection is disabled. (It's beyond the scope of this question to go into more details.)

Alright then, even though the side effects are not entirely clear, a rule of the thumb is that it's safe to disable change tracking if code doesn't rely on relationship fixup to occur. The code you show won't be a problem. But here's an example of the type of code that may raise exceptions:

Context.ChangeTracker.AutoDetectChangesEnabled = false;

var recipe = context.Find(recipeId);

ItemRecipe item = new();
context.ItemRecipes.Add(item);

item.RecipeId = recipeId;

item.Recipe.Formula = new Formula(); // null reference?
// item.Recipe may not have been set by relationship fixup.

Of course, only disable change tracking when necessary. It should not be the standard.

  • Related