EF Core 3.x Include query splitting without using tracking

2.4k views Asked by At

When we migrated from ef core 2.x to 3.x, our queries slowed down and some of them are broken due to cartesian explosion problem. As you know, ef core 2.x was splitting query into several queries, however with ef core 3.x single query is generated with multiple joins.

As a work around, we manually split the queries, however, for this to work, tracking must be enabled so ef core can stitch the object together. Simple split query example below;

public async Task<Order> GetOrder(int orderId, bool disableTracking = true){
    var order = await _dbContext.Orders.Where(c => c.Id == orderId).FirstOrDefaultAsync();
    var orderDetails = await _dbContext.OrderDetails.Where(c => c.OrderId == orderId).ToListAsync();
    if (disableTracking) // query with tracking enabled, then detach object.
        DetachObject(order);
    return order;
}

Our dbContext is scoped and sometimes we need order to be untracked. But if we use .AsNoTracking() above code does not work. And since dbContext is scoped, I can't safely untrack everything in ChangeTracked. So another workaround below;

public void DetachObject(object entity){
    // detaching just this object from ChangeTracker
    _dbContext.ChangeTracker.TrackGraph(entity, EntityState.Detached, c =>
        {
            if (c.Entry.State == EntityState.Detached)
                return false;
            c.Entry.State = EntityState.Detached;
            return true;
        });
}

The question is; Is this DetachObject() method is safe to use? I couldn't find any example online so I had to get creative. Method works and detachs everything order related from ChangeTracker but there is a warning in the documentation of TrackGraph() method.

... This overload, on the other hand, allows the callback to decide when traversal will end, but the onus is then on the caller to ensure that traversal will not enter an infinite loop.

0

There are 0 answers