csharpier icon indicating copy to clipboard operation
csharpier copied to clipboard

Indent .ThenInclude() for clearer navigation hierarchy in EF Core queries

Open HerrLiljegren opened this issue 8 months ago • 1 comments

Suggestion: Improve formatting of chained .Include() and .ThenInclude() in EF Core queries

When using Entity Framework Core, readability is greatly improved by clearly indicating navigation relationships. Currently, CSharpier formats chained .Include() and .ThenInclude() methods without indentation differentiation, which can obscure their relationship.

The additional indentation on .ThenInclude() visually represents the hierarchy and makes the code clearer at a glance.

Input:

var user = context.Users.Include(u => u.Orders).ThenInclude(o => o.OrderItems).Where(u => u.Id == userId).FirstOrDefault();

Output:

var user = context.Users
    .Include(u => u.Orders)
    .ThenInclude(o => o.OrderItems)
    .Where(u => u.Id == userId)
    .FirstOrDefault();

Expected behavior:

var user = context.Users
    .Include(u => u.Orders)
        .ThenInclude(o => o.OrderItems)
    .Where(u => u.Id == userId)
    .FirstOrDefault();

The additional indentation on .ThenInclude() visually represents the navigation hierarchy clearly.


Would this be something worth considering?

And also, thanks for your awesome work!

HerrLiljegren avatar May 08 '25 06:05 HerrLiljegren

Having library specific formatting feels a little bit wrong, but EF is so widely used this does seem reasonable.

For an even longer example this is a method chain I ran into at my job recently.

            websiteQueryable = websiteQueryable
                .Include(o => o.Categories)
                .Include(o => o.Categories)
                .ThenInclude(c => c.Products)
                .Include(o => o.Categories)
                .ThenInclude(c => c.RuleManager)
                .Include(o => o.Categories)
                .ThenInclude(c => c.RuleManager)
                .ThenInclude(rm => rm.RuleType)
                .Include(o => o.Categories)
                .ThenInclude(c => c.RuleManager)
                .ThenInclude(rm => rm.RuleClauses)
                .Include(o => o.Categories)
                .ThenInclude(c => c.RuleManager)
                .ThenInclude(rm => rm.RuleClauses)
                .ThenInclude(rc => rc.RuleTypeOption)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .ThenInclude(cm => cm.Contents)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .ThenInclude(cm => cm.Contents)
                .ThenInclude(c => c.Language)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .ThenInclude(cm => cm.Contents)
                .ThenInclude(c => c.Persona)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .ThenInclude(cm => cm.Contents)
                .ThenInclude(c => c.CreatedByAdminUserProfile)
                .Include(o => o.Categories)
                .ThenInclude(c => c.ContentManager)
                .ThenInclude(cm => cm.Contents)
                .ThenInclude(c => c.ApprovedByAdminUserProfile);

belav avatar Jul 19 '25 18:07 belav