55using Dapper ;
66using System . Threading . Tasks ;
77using System . Dynamic ;
8+ using Humanizer ;
89
910namespace SqlKata . Execution
1011{
@@ -22,44 +23,7 @@ public static async Task<IEnumerable<T>> GetAsync<T>(this QueryFactory db, Query
2223 commandTimeout : db . QueryTimeout
2324 ) ) . ToList ( ) ;
2425
25- if ( ! result . Any ( ) )
26- {
27- return result ;
28- }
29-
30- if ( result [ 0 ] is IDynamicMetaObjectProvider )
31- {
32- var dynamicResult = result
33- . Cast < IDictionary < string , object > > ( )
34- . Select ( x => new Dictionary < string , object > ( x , StringComparer . OrdinalIgnoreCase ) )
35- . ToList ( ) ;
36-
37- foreach ( var include in query . Includes )
38- {
39- var ids = dynamicResult . Where ( x => x [ include . ForeignKey ] != null )
40- . Select ( x => x [ include . ForeignKey ] . ToString ( ) )
41- . ToList ( ) ;
42-
43- if ( ! ids . Any ( ) )
44- {
45- continue ;
46- }
47-
48- var related = ( await include . Query . WhereIn ( include . LocalKey , ids ) . GetAsync ( ) )
49- . Cast < IDictionary < string , object > > ( )
50- . Select ( x => new Dictionary < string , object > ( x , StringComparer . OrdinalIgnoreCase ) )
51- . ToDictionary ( x => x [ include . LocalKey ] . ToString ( ) ) ;
52-
53- foreach ( var item in dynamicResult )
54- {
55- var foreignValue = item [ include . ForeignKey ] . ToString ( ) ;
56- item [ include . Name ] = related . ContainsKey ( foreignValue ) ? related [ foreignValue ] : null ;
57- }
58- }
59-
60- return dynamicResult . Cast < T > ( ) ;
61-
62- }
26+ result = ( await handleIncludesAsync ( query , result ) ) . ToList ( ) ;
6327
6428 return result ;
6529 }
@@ -332,5 +296,102 @@ public static async Task<int> StatementAsync(this QueryFactory db, string sql, o
332296 return await db . Connection . ExecuteAsync ( sql , param , commandTimeout : db . QueryTimeout ) ;
333297 }
334298 #endregion
299+
300+ // TODO: currently am copying this from the handleInclude (sync) method, refactor this and reuse the common part.
301+ private static async Task < IEnumerable < T > > handleIncludesAsync < T > ( Query query , IEnumerable < T > result )
302+ {
303+ if ( ! result . Any ( ) )
304+ {
305+ return result ;
306+ }
307+
308+ var canBeProcessed = query . Includes . Any ( ) && result . ElementAt ( 0 ) is IDynamicMetaObjectProvider ;
309+
310+ if ( ! canBeProcessed )
311+ {
312+ return result ;
313+ }
314+
315+ var dynamicResult = result
316+ . Cast < IDictionary < string , object > > ( )
317+ . Select ( x => new Dictionary < string , object > ( x , StringComparer . OrdinalIgnoreCase ) )
318+ . ToList ( ) ;
319+
320+ foreach ( var include in query . Includes )
321+ {
322+
323+ if ( include . IsMany )
324+ {
325+ if ( include . ForeignKey == null )
326+ {
327+ // try to guess the default key
328+ // I will try to fetch the table name if provided and appending the Id as a convention
329+ // Here am using Humanizer package to help getting the singular form of the table
330+
331+ var fromTable = query . GetOneComponent ( "from" ) as FromClause ;
332+
333+ if ( fromTable == null )
334+ {
335+ throw new InvalidOperationException ( $ "Cannot guess the foreign key for the included relation '{ include . Name } '") ;
336+ }
337+
338+ var table = fromTable . Alias ?? fromTable . Table ;
339+
340+ include . ForeignKey = table . Singularize ( false ) + "Id" ;
341+ }
342+
343+ var localIds = dynamicResult . Where ( x => x [ include . LocalKey ] != null )
344+ . Select ( x => x [ include . LocalKey ] . ToString ( ) )
345+ . ToList ( ) ;
346+
347+ if ( ! localIds . Any ( ) )
348+ {
349+ continue ;
350+ }
351+
352+ var children = ( await include . Query . WhereIn ( include . ForeignKey , localIds ) . GetAsync ( ) )
353+ . Cast < IDictionary < string , object > > ( )
354+ . Select ( x => new Dictionary < string , object > ( x , StringComparer . OrdinalIgnoreCase ) )
355+ . GroupBy ( x => x [ include . ForeignKey ] . ToString ( ) )
356+ . ToDictionary ( x => x . Key , x => x . ToList ( ) ) ;
357+
358+ foreach ( var item in dynamicResult )
359+ {
360+ var localValue = item [ include . LocalKey ] . ToString ( ) ;
361+ item [ include . Name ] = children . ContainsKey ( localValue ) ? children [ localValue ] : new List < Dictionary < string , object > > ( ) ;
362+ }
363+
364+ continue ;
365+ }
366+
367+ if ( include . ForeignKey == null )
368+ {
369+ include . ForeignKey = include . Name + "Id" ;
370+ }
371+
372+ var foreignIds = dynamicResult . Where ( x => x [ include . ForeignKey ] != null )
373+ . Select ( x => x [ include . ForeignKey ] . ToString ( ) )
374+ . ToList ( ) ;
375+
376+ if ( ! foreignIds . Any ( ) )
377+ {
378+ continue ;
379+ }
380+
381+ var related = ( await include . Query . WhereIn ( include . LocalKey , foreignIds ) . GetAsync ( ) )
382+ . Cast < IDictionary < string , object > > ( )
383+ . Select ( x => new Dictionary < string , object > ( x , StringComparer . OrdinalIgnoreCase ) )
384+ . ToDictionary ( x => x [ include . LocalKey ] . ToString ( ) ) ;
385+
386+ foreach ( var item in dynamicResult )
387+ {
388+ var foreignValue = item [ include . ForeignKey ] . ToString ( ) ;
389+ item [ include . Name ] = related . ContainsKey ( foreignValue ) ? related [ foreignValue ] : null ;
390+ }
391+ }
392+
393+ return dynamicResult . Cast < T > ( ) ;
394+
395+ }
335396 }
336397}
0 commit comments