Skip to content

Commit 1e2e4b6

Browse files
fix: 🐛 single included resources are no longer ignored
1 parent 549743c commit 1e2e4b6

2 files changed

Lines changed: 72 additions & 10 deletions

File tree

JsonApiToolkit/Extensions/Querying/FilteredIncludeBuilder.cs

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,11 +192,14 @@ Type rootType
192192
var filteredCollection = Expression.Call(whereMethod, secondNavAccess, whereLambda);
193193
var thenIncludeLambda = Expression.Lambda(filteredCollection, navParam);
194194

195-
// Apply ThenInclude
196-
var thenIncludeMethod = typeof(EntityFrameworkQueryableExtensions)
197-
.GetMethods()
198-
.First(m => m.Name == "ThenInclude" && m.GetGenericArguments().Length == 3)
199-
.MakeGenericMethod(rootType, firstNavType, filteredCollection.Type);
195+
// Apply ThenInclude with correct overload selection
196+
var isFirstCollection = IsCollectionType(firstProperty.PropertyType);
197+
var thenIncludeMethod = GetThenIncludeMethod(
198+
isFirstCollection,
199+
rootType,
200+
firstNavType,
201+
filteredCollection.Type
202+
);
200203

201204
var result = thenIncludeMethod.Invoke(
202205
null,
@@ -209,10 +212,14 @@ Type rootType
209212
// No valid filter, use unfiltered ThenInclude
210213
var thenIncludeLambda = Expression.Lambda(secondNavAccess, navParam);
211214

212-
var thenIncludeMethod = typeof(EntityFrameworkQueryableExtensions)
213-
.GetMethods()
214-
.First(m => m.Name == "ThenInclude" && m.GetGenericArguments().Length == 3)
215-
.MakeGenericMethod(rootType, firstNavType, secondProperty.PropertyType);
215+
// Apply ThenInclude with correct overload selection
216+
var isFirstCollection = IsCollectionType(firstProperty.PropertyType);
217+
var thenIncludeMethod = GetThenIncludeMethod(
218+
isFirstCollection,
219+
rootType,
220+
firstNavType,
221+
secondProperty.PropertyType
222+
);
216223

217224
var result = thenIncludeMethod.Invoke(
218225
null,
@@ -362,6 +369,61 @@ private static Type GetNavigationTargetType(Type navigationType)
362369
return GetCollectionElementType(navigationType) ?? navigationType;
363370
}
364371

372+
/// <summary>
373+
/// Gets the correct ThenInclude method based on whether the previous navigation is a collection or reference.
374+
/// EF Core has two overloads:
375+
/// - ThenInclude for IIncludableQueryable with IEnumerable (collection navigation)
376+
/// - ThenInclude for IIncludableQueryable with single entity (reference navigation)
377+
/// </summary>
378+
private static MethodInfo GetThenIncludeMethod(
379+
bool isPreviousCollection,
380+
Type entityType,
381+
Type previousPropertyType,
382+
Type newPropertyType
383+
)
384+
{
385+
var thenIncludeMethods = typeof(EntityFrameworkQueryableExtensions)
386+
.GetMethods()
387+
.Where(m => m.Name == "ThenInclude" && m.GetGenericArguments().Length == 3)
388+
.ToList();
389+
390+
foreach (var method in thenIncludeMethods)
391+
{
392+
var parameters = method.GetParameters();
393+
if (parameters.Length != 2)
394+
continue;
395+
396+
var firstParamType = parameters[0].ParameterType;
397+
if (
398+
!firstParamType.IsGenericType
399+
|| firstParamType.GetGenericTypeDefinition().Name != "IIncludableQueryable`2"
400+
)
401+
continue;
402+
403+
var genericArgs = firstParamType.GetGenericArguments();
404+
if (genericArgs.Length != 2)
405+
continue;
406+
407+
var secondGenericArg = genericArgs[1];
408+
409+
// Check if this overload matches our navigation type (collection vs reference)
410+
bool isCollectionOverload =
411+
secondGenericArg.IsGenericType
412+
&& secondGenericArg.GetGenericTypeDefinition() == typeof(IEnumerable<>);
413+
414+
if (isCollectionOverload == isPreviousCollection)
415+
{
416+
return method.MakeGenericMethod(entityType, previousPropertyType, newPropertyType);
417+
}
418+
}
419+
420+
// Fallback to original logic if no match found
421+
return typeof(EntityFrameworkQueryableExtensions)
422+
.GetMethods()
423+
.First(m => m.Name == "ThenInclude" && m.GetGenericArguments().Length == 3)
424+
.MakeGenericMethod(entityType, previousPropertyType, newPropertyType);
425+
}
426+
365427
private static IQueryable<T> ApplyFilteredIncludeWithFilters<T>(
366428
IQueryable<T> query,
367429
string navigationPath,

JsonApiToolkit/JsonApiToolkit.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
<!-- Package metadata -->
99
<PackageId>Intility.JsonApiToolkit</PackageId>
10-
<Version>1.1.6-rc1</Version>
10+
<Version>1.1.7-rc1</Version>
1111
<Authors>Intility</Authors>
1212
<Company>Intility</Company>
1313
<Description>A toolkit for implementing JSON:API specification in .NET applications</Description>

0 commit comments

Comments
 (0)