Skip to main content
We’ve updated our Terms of Service. A new AI Addendum clarifies how Stack Overflow utilizes AI interactions.
Re-added a comment that went away when adding cancellation support
Source Link
Irwene
  • 195
  • 8
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity, CancellationToken cancellationToken) where TEntityToLoad : class, IEntity
{
    // this.Context is of type Microsoft.EntityFrameworkCore.DbContext
    EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

    foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
    {
        cancellationToken.ThrowIfCancellationRequested();
        
        await LoadRecursiveAsyncInternal(navigationEntry);
    }

    return;

    async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
    {
        await navigationEntry.LoadAsync(cancellationToken);

        object? currentValue = navigationEntry.CurrentValue;

        switch (currentValue)
        {
            case IEntity loadedEntity:
            {
                foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    
                    await LoadRecursiveAsyncInternal(navigation);
                }

                break;
            }
            case null:
                break;
            default:
            {
                Type type = currentValue.GetType();
            
                if(!type.IsAssignableTo(typeof(IEnumerable)))
                {
                    return;
                }

                if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                {
                    foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                    {
                        foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            
                            await LoadRecursiveAsyncInternal(navigation);
                        }
                    }
                }

                break;
            }
        }
    }
}
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity, CancellationToken cancellationToken) where TEntityToLoad : class, IEntity
{
    EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

    foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
    {
        cancellationToken.ThrowIfCancellationRequested();
        
        await LoadRecursiveAsyncInternal(navigationEntry);
    }

    return;

    async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
    {
        await navigationEntry.LoadAsync(cancellationToken);

        object? currentValue = navigationEntry.CurrentValue;

        switch (currentValue)
        {
            case IEntity loadedEntity:
            {
                foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    
                    await LoadRecursiveAsyncInternal(navigation);
                }

                break;
            }
            case null:
                break;
            default:
            {
                Type type = currentValue.GetType();
            
                if(!type.IsAssignableTo(typeof(IEnumerable)))
                {
                    return;
                }

                if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                {
                    foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                    {
                        foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            
                            await LoadRecursiveAsyncInternal(navigation);
                        }
                    }
                }

                break;
            }
        }
    }
}
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity, CancellationToken cancellationToken) where TEntityToLoad : class, IEntity
{
    // this.Context is of type Microsoft.EntityFrameworkCore.DbContext
    EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

    foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
    {
        cancellationToken.ThrowIfCancellationRequested();
        
        await LoadRecursiveAsyncInternal(navigationEntry);
    }

    return;

    async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
    {
        await navigationEntry.LoadAsync(cancellationToken);

        object? currentValue = navigationEntry.CurrentValue;

        switch (currentValue)
        {
            case IEntity loadedEntity:
            {
                foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    
                    await LoadRecursiveAsyncInternal(navigation);
                }

                break;
            }
            case null:
                break;
            default:
            {
                Type type = currentValue.GetType();
            
                if(!type.IsAssignableTo(typeof(IEnumerable)))
                {
                    return;
                }

                if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                {
                    foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                    {
                        foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                            
                            await LoadRecursiveAsyncInternal(navigation);
                        }
                    }
                }

                break;
            }
        }
    }
}
Added Benchmark
Source Link
Irwene
  • 195
  • 8

Edit 2: Comparison of manual includes with the above recursive method, made with BenchmarkDotNet (Runtime: .NET 6.0, IterationCount 100)

MethodMeanErrorStdDevMedianRatioRatioSDGen0Gen1AllocatedAlloc Ratio
LoadExplicitAsync9.655 ms0.1228 ms0.3504 ms9.509 ms1.000.00328.1250140.62502.61 MB1.00
LoadRecursiveAsync56.355 ms0.3518 ms0.9866 ms56.376 ms5.850.25700.0000200.00005.85 MB2.24

Edit 2: Comparison of manual includes with the above recursive method, made with BenchmarkDotNet (Runtime: .NET 6.0, IterationCount 100)

MethodMeanErrorStdDevMedianRatioRatioSDGen0Gen1AllocatedAlloc Ratio
LoadExplicitAsync9.655 ms0.1228 ms0.3504 ms9.509 ms1.000.00328.1250140.62502.61 MB1.00
LoadRecursiveAsync56.355 ms0.3518 ms0.9866 ms56.376 ms5.850.25700.0000200.00005.85 MB2.24
Added cancellation support
Source Link
Irwene
  • 195
  • 8
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity, CancellationToken cancellationToken) where TEntityToLoad : class, IEntity
    {
        // this.Context is of type Microsoft.EntityFrameworkCore.DbContext
        EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

        foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
    {
     {   cancellationToken.ThrowIfCancellationRequested();
        
        await LoadRecursiveAsyncInternal(navigationEntry);
        }

        return;

        async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
        {
            await navigationEntry.LoadAsync(cancellationToken);

            object? currentValue = navigationEntry.CurrentValue;

            switch (currentValue)
            {
                case IEntity loadedEntity:
                {
                    foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                    {
                        await LoadRecursiveAsyncInternalcancellationToken.ThrowIfCancellationRequested(navigation);
                    }

                    break;await LoadRecursiveAsyncInternal(navigation);
                }
  
               case null:break;
            }
        break;    case null:
                default:break;
            default:
     {
       {
                Type type = currentValue.GetType();
                
                    if(!type.IsAssignableTo(typeof(IEnumerable)))
                    {
                        return;
                    }

                    if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                    {
                        foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                        {
                            foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                        {
    {
                        cancellationToken.ThrowIfCancellationRequested();
        await LoadRecursiveAsyncInternal(navigation);
                    
         }
                   await LoadRecursiveAsyncInternal(navigation);
    }
                    }
 
                    break;}
                }

                break;
            }
        }
    }
}
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity) where TEntityToLoad : class, IEntity
    {
        // this.Context is of type Microsoft.EntityFrameworkCore.DbContext
        EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

        foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
        {
            await LoadRecursiveAsyncInternal(navigationEntry);
        }

        return;

        async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
        {
            await navigationEntry.LoadAsync();

            object? currentValue = navigationEntry.CurrentValue;

            switch (currentValue)
            {
                case IEntity loadedEntity:
                {
                    foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                    {
                        await LoadRecursiveAsyncInternal(navigation);
                    }

                    break;
                }
                case null:
                    break;
                default:
                {
                    Type type = currentValue.GetType();
                
                    if(!type.IsAssignableTo(typeof(IEnumerable)))
                    {
                        return;
                    }

                    if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                    {
                        foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                        {
                            foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                            {
                                await LoadRecursiveAsyncInternal(navigation);
                            }
                        }
                    }
 
                    break;
                }
            }
        }
    }
protected async Task LoadRecursiveAsync<TEntityToLoad>(TEntityToLoad entity, CancellationToken cancellationToken) where TEntityToLoad : class, IEntity
{
    EntityEntry<TEntityToLoad> entry = this.Context.Entry(entity);

    foreach (NavigationEntry navigationEntry in entry.Navigations.Where(ne => !ne.IsLoaded))
    {
        cancellationToken.ThrowIfCancellationRequested();
        
        await LoadRecursiveAsyncInternal(navigationEntry);
    }

    return;

    async Task LoadRecursiveAsyncInternal(NavigationEntry navigationEntry)
    {
        await navigationEntry.LoadAsync(cancellationToken);

        object? currentValue = navigationEntry.CurrentValue;

        switch (currentValue)
        {
            case IEntity loadedEntity:
            {
                foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    
                    await LoadRecursiveAsyncInternal(navigation);
                }
 
                break;
            }
            case null:
                break;
            default:
            {
                Type type = currentValue.GetType();
            
                if(!type.IsAssignableTo(typeof(IEnumerable)))
                {
                    return;
                }

                if (type.GetGenericArguments() is [{ } entityType] && entityType.IsAssignableTo(typeof(IEntity)))
                {
                    foreach (IEntity loadedEntity in (IEnumerable)currentValue)
                    {
                        foreach (NavigationEntry navigation in this.Context.Entry(loadedEntity).Navigations.Where(ne => !ne.IsLoaded))
                        {
                            cancellationToken.ThrowIfCancellationRequested();
                             
                            await LoadRecursiveAsyncInternal(navigation);
                        }
                    }
                }

                break;
            }
        }
    }
}
added 211 characters in body
Source Link
Irwene
  • 195
  • 8
Loading
Source Link
Irwene
  • 195
  • 8
Loading