Skip to content

Commit 630427a

Browse files
refactor(controller): dedupe filter+include logic between JsonApiQueryAsync and BuildJsonApiQueryAsync
1 parent 9b602bd commit 630427a

1 file changed

Lines changed: 30 additions & 59 deletions

File tree

JsonApiToolkit/Controllers/JsonApiController.cs

Lines changed: 30 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,8 @@ string resourceType
197197
IQueryable<T> filteredQuery = ApplyFiltersAndIncludes(
198198
queryable,
199199
parameters,
200-
mappedIncludes
200+
mappedIncludes,
201+
paginating: parameters.Pagination != null
201202
);
202203

203204
if (parameters.Sort?.Count > 0)
@@ -280,53 +281,15 @@ protected async Task<JsonApiQueryResult<T>> BuildJsonApiQueryAsync<T>(
280281
parameters.Include
281282
);
282283

283-
if (parameters.Include?.Count > 0 && mappedIncludes.Count == 0)
284-
{
285-
Logger.LogWarning(
286-
"No valid includes for {EntityType}. Requested: {Includes}",
287-
typeof(T).Name,
288-
string.Join(", ", parameters.Include)
289-
);
290-
}
284+
LogInvalidIncludes<T>(parameters, mappedIncludes);
291285

292-
var (mainFilters, includeFilters) = IncludeFilterParser.SeparateIncludeFilters(
293-
parameters.Filter,
294-
parameters.Include
286+
IQueryable<T> processedQuery = ApplyFiltersAndIncludes(
287+
queryable,
288+
parameters,
289+
mappedIncludes,
290+
paginating: false
295291
);
296292

297-
IQueryable<T> processedQuery = queryable;
298-
299-
// Apply main entity filters
300-
if (mainFilters != null)
301-
processedQuery = processedQuery.ApplyFilters(mainFilters, Logger);
302-
303-
// Apply includes (with or without filters)
304-
if (includeFilters.Count > 0)
305-
{
306-
Logger.LogDebug(
307-
"Applying {FilterCount} filtered includes for {EntityType}",
308-
includeFilters.Count,
309-
typeof(T).Name
310-
);
311-
processedQuery = processedQuery.ApplyFilteredIncludes(
312-
mappedIncludes,
313-
includeFilters,
314-
Logger
315-
);
316-
}
317-
else if (mappedIncludes.Count > 0)
318-
{
319-
// Use standard includes (no pagination optimization needed since we're not paginating)
320-
processedQuery = processedQuery.ApplyIncludes(mappedIncludes);
321-
322-
Logger.LogDebug(
323-
"Applied {IncludeCount} includes for {EntityType}",
324-
mappedIncludes.Count,
325-
typeof(T).Name
326-
);
327-
}
328-
329-
// Apply sorting
330293
if (parameters.Sort?.Count > 0)
331294
processedQuery = processedQuery.ApplySorting(parameters.Sort, Logger);
332295

@@ -435,20 +398,17 @@ private void LogQueryParameters<T>(QueryParameters parameters, List<string> mapp
435398
);
436399
}
437400

438-
if (parameters.Include?.Count > 0 && mappedIncludes.Count == 0)
439-
{
440-
Logger.LogWarning(
441-
"No valid includes for {EntityType}. Requested: {Includes}",
442-
typeof(T).Name,
443-
string.Join(", ", parameters.Include)
444-
);
445-
}
401+
LogInvalidIncludes<T>(parameters, mappedIncludes);
446402
}
447403

404+
// When `paginating` is true, includes use single-query mode to avoid the
405+
// EF Core warning/exception triggered by split-query + Skip/Take. Otherwise
406+
// split-query is preferred to avoid cartesian explosion on collection includes.
448407
private IQueryable<T> ApplyFiltersAndIncludes<T>(
449408
IQueryable<T> queryable,
450409
QueryParameters parameters,
451-
List<string> mappedIncludes
410+
List<string> mappedIncludes,
411+
bool paginating
452412
)
453413
where T : class
454414
{
@@ -477,22 +437,33 @@ List<string> mappedIncludes
477437
}
478438
else if (mappedIncludes.Count > 0)
479439
{
480-
filteredQuery =
481-
parameters.Pagination != null
482-
? filteredQuery.ApplyIncludesSingleQuery(mappedIncludes)
483-
: filteredQuery.ApplyIncludes(mappedIncludes);
440+
filteredQuery = paginating
441+
? filteredQuery.ApplyIncludesSingleQuery(mappedIncludes)
442+
: filteredQuery.ApplyIncludes(mappedIncludes);
484443

485444
Logger.LogDebug(
486445
"Applied {IncludeCount} includes for {EntityType} using {QueryType}",
487446
mappedIncludes.Count,
488447
typeof(T).Name,
489-
parameters.Pagination != null ? "SingleQuery" : "SplitQuery"
448+
paginating ? "SingleQuery" : "SplitQuery"
490449
);
491450
}
492451

493452
return filteredQuery;
494453
}
495454

455+
private void LogInvalidIncludes<T>(QueryParameters parameters, List<string> mappedIncludes)
456+
{
457+
if (parameters.Include?.Count > 0 && mappedIncludes.Count == 0)
458+
{
459+
Logger.LogWarning(
460+
"No valid includes for {EntityType}. Requested: {Includes}",
461+
typeof(T).Name,
462+
string.Join(", ", parameters.Include)
463+
);
464+
}
465+
}
466+
496467
private static void EnforceStrictPagination(
497468
JsonApiOptions options,
498469
QueryParameters parameters,

0 commit comments

Comments
 (0)