Skip to content

Commit 17c7b2a

Browse files
committed
Add IncludePaths extension for string-based EF includes
Added IncludePaths<T> to QueryableExtensions for including related entities using string navigation paths. Paths are normalized, deduplicated, and ordered to ensure parent paths precede children. Also introduced a helper for path normalization and removed some XML docs for brevity. No functional changes to existing ordering methods.
1 parent 976de4f commit 17c7b2a

1 file changed

Lines changed: 41 additions & 14 deletions

File tree

src/TinyRepository/Extensions/QueryableExtensions.cs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ namespace TinyRepository.Extensions;
88

99
public static class QueryableExtensions
1010
{
11-
/// <summary>
12-
/// Applica Include multipli.
13-
/// </summary>
1411
public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params Expression<Func<T, object>>[] includes) where T : class
1512
{
1613
if (includes == null || includes.Length == 0)
@@ -27,10 +24,28 @@ public static IQueryable<T> IncludeMultiple<T>(this IQueryable<T> query, params
2724
return query;
2825
}
2926

30-
/// <summary>
31-
/// Applica ordering dinamico per singola proprietà (supporta percorsi con dot, es. "Author.Name").
32-
/// Se orderByProperty è null/empty restituisce la query originale.
33-
/// </summary>
27+
public static IQueryable<T> IncludePaths<T>(this IQueryable<T> query, params string[] includePaths) where T : class
28+
{
29+
if (includePaths == null || includePaths.Length == 0)
30+
{
31+
return query;
32+
}
33+
34+
// Rimuoviamo duplicati e ordiniamo in modo che i percorsi "padre" vengano prima dei figli.
35+
// Questo non è strettamente necessario per EF Core, ma aiuta in scenari di costruzione di query leggibili.
36+
var normalized = NormalizeAndOrderPaths(includePaths);
37+
38+
foreach (var path in normalized)
39+
{
40+
if (!string.IsNullOrWhiteSpace(path))
41+
{
42+
query = query.Include(path);
43+
}
44+
}
45+
46+
return query;
47+
}
48+
3449
public static IQueryable<T> ApplyOrderByProperty<T>(this IQueryable<T> source, string? orderByProperty, bool descending = false)
3550
{
3651
if (string.IsNullOrWhiteSpace(orderByProperty))
@@ -71,11 +86,6 @@ public static IQueryable<T> ApplyOrderByProperty<T>(this IQueryable<T> source, s
7186
return ordered;
7287
}
7388

74-
/// <summary>
75-
/// Applica multiple ordinamenti (OrderBy / ThenBy) in base ai SortDescriptor.
76-
/// Se allowedProperties è non-null e non vuota, ogni property deve far parte della whitelist o viene lanciata un'eccezione.
77-
/// Supporta proprietà annidate con dot path ("Author.LastName").
78-
/// </summary>
7989
public static IQueryable<T> ApplyOrdering<T>(this IQueryable<T> source, IEnumerable<SortDescriptor> sortDescriptors,
8090
IEnumerable<string>? allowedProperties = null)
8191
{
@@ -141,8 +151,7 @@ public static IQueryable<T> ApplyOrdering<T>(this IQueryable<T> source, IEnumera
141151

142152
var queryableType = typeof(Queryable);
143153
var methods = queryableType.GetMethods(BindingFlags.Public | BindingFlags.Static)
144-
.Where(m => m.Name == methodName && m.GetParameters().Length == 2)
145-
.ToList();
154+
.Where(m => m.Name == methodName && m.GetParameters().Length == 2).ToList();
146155

147156
var method = methods.FirstOrDefault();
148157

@@ -165,4 +174,22 @@ public static IQueryable<T> ApplyOrdering<T>(this IQueryable<T> source, IEnumera
165174

166175
return orderedQuery ?? source;
167176
}
177+
178+
private static string[] NormalizeAndOrderPaths(string[] includePaths)
179+
{
180+
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
181+
foreach (var p in includePaths)
182+
{
183+
if (string.IsNullOrWhiteSpace(p))
184+
{
185+
continue;
186+
}
187+
188+
var trimmed = p.Trim();
189+
set.Add(trimmed);
190+
}
191+
192+
// Order by number of segments ascending: parents before children
193+
return set.OrderBy(p => p.Count(c => c == '.')).ThenBy(p => p, StringComparer.OrdinalIgnoreCase).ToArray();
194+
}
168195
}

0 commit comments

Comments
 (0)