1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
4+ using System . Linq . Expressions ;
5+ using System . Reflection ;
6+
7+ namespace Z . EntityFramework . Plus
8+ {
9+ /// <summary>A query include filter by path.</summary>
10+ public static class QueryIncludeFilterByPath
11+ {
12+ /// <summary>Include filter by path.</summary>
13+ /// <typeparam name="T">Generic type parameter.</typeparam>
14+ /// <param name="query">The query.</param>
15+ /// <param name="navigationPath">Full pathname of the navigation file.</param>
16+ /// <returns>An IQueryable<T></returns>
17+ public static IQueryable < T > IncludeFilterByPath < T > ( IQueryable < T > query , string navigationPath )
18+ {
19+ var elementType = typeof ( T ) ;
20+ var paths = navigationPath . Split ( '.' ) ;
21+
22+ // CREATE expression x => x.Right
23+ var expression = CreateLambdaExpression ( elementType , paths , 0 ) ;
24+
25+ var method = typeof ( QueryIncludeFilterExtensions ) . GetMethod ( "IncludeFilter" , BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Static ) ? . MakeGenericMethod ( typeof ( T ) , expression . Type . GetGenericArguments ( ) [ 1 ] ) ;
26+
27+ query = ( IQueryable < T > ) method ? . Invoke ( null , new object [ ] { query , expression } ) ;
28+
29+ return query ;
30+ }
31+
32+ /// <summary>Creates lambda expression.</summary>
33+ /// <param name="parameterType">Type of the parameter.</param>
34+ /// <param name="paths">The paths.</param>
35+ /// <param name="currentIndex">The current index.</param>
36+ /// <returns>The new lambda expression.</returns>
37+ public static Expression CreateLambdaExpression ( Type parameterType , string [ ] paths , int currentIndex )
38+ {
39+ // CREATE expression [x => x.Right]
40+
41+ // ADD parameter [x =>]
42+ var parameter = Expression . Parameter ( parameterType ) ;
43+ Expression expression = parameter ;
44+
45+ // ADD property [x.Right]
46+ expression = AppendPropertyPath ( expression , paths , currentIndex ) ;
47+
48+ // GET function generic type
49+ var funcGenericType = typeof ( Func < , > ) . MakeGenericType ( parameterType , expression . Type ) ;
50+
51+ // GET lambda method
52+ var lambdaMethod = typeof ( Expression ) . GetMethods ( )
53+ . Single ( x => x . Name == "Lambda"
54+ && x . IsGenericMethod
55+ && x . GetParameters ( ) . Length == 2
56+ && ! x . GetParameters ( ) [ 1 ] . ParameterType . IsArray )
57+ . MakeGenericMethod ( funcGenericType ) ;
58+
59+ // CREATE lambda expression
60+ expression = ( Expression ) lambdaMethod . Invoke ( null , new object [ ] { expression , new List < ParameterExpression > { parameter } } ) ;
61+
62+ return expression ;
63+ }
64+
65+ /// <summary>Appends a path.</summary>
66+ /// <param name="expression">The expression.</param>
67+ /// <param name="paths">The paths.</param>
68+ /// <param name="currentIndex">The current index.</param>
69+ /// <returns>An Expression.</returns>
70+ public static Expression AppendPath ( Expression expression , string [ ] paths , int currentIndex )
71+ {
72+ expression = expression . Type . GetGenericArguments ( ) . Length == 0 ? AppendPropertyPath ( expression , paths , currentIndex ) : AppendSelectPath ( expression , paths , currentIndex ) ;
73+
74+ return expression ;
75+ }
76+
77+ /// <summary>Appends a property path.</summary>
78+ /// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
79+ /// <param name="expression">The expression.</param>
80+ /// <param name="paths">The paths.</param>
81+ /// <param name="currentIndex">The current index.</param>
82+ /// <returns>An Expression.</returns>
83+ public static Expression AppendPropertyPath ( Expression expression , string [ ] paths , int currentIndex )
84+ {
85+ // APPEND [x.PropertyName]
86+ var elementType = expression . Type ;
87+ var property = elementType . GetProperty ( paths [ currentIndex ] , BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) ;
88+
89+ // ENSURE property exists
90+ if ( property == null )
91+ {
92+ // Try Again with case insensitive
93+ var properties = elementType . GetProperties ( BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance )
94+ . Where ( x => x . Name . ToLowerInvariant ( ) == paths [ currentIndex ] . ToLowerInvariant ( ) ) . ToList ( ) ;
95+
96+ if ( properties . Count == 1 )
97+ {
98+ property = properties [ 0 ] ;
99+ }
100+
101+ if ( property == null )
102+ {
103+ throw new Exception ( string . Format ( ExceptionMessage . QueryIncludeFilter_ByPath_MissingPath , elementType . FullName , paths [ currentIndex ] ) ) ;
104+ }
105+ }
106+
107+ expression = Expression . Property ( expression , property ) ;
108+
109+ // APPEND path child
110+ currentIndex ++ ;
111+ if ( currentIndex < paths . Length )
112+ {
113+ expression = AppendPath ( expression , paths , currentIndex ) ;
114+ }
115+
116+ return expression ;
117+ }
118+
119+ /// <summary>Appends a select path.</summary>
120+ /// <param name="expression">The expression.</param>
121+ /// <param name="paths">The paths.</param>
122+ /// <param name="currentIndex">The current index.</param>
123+ /// <returns>An Expression.</returns>
124+ public static Expression AppendSelectPath ( Expression expression , string [ ] paths , int currentIndex )
125+ {
126+ // APPEND x => x.Rights[.Select(y => y.Right)]
127+ var elementType = expression . Type . GetGenericArguments ( ) [ 0 ] ;
128+
129+ // CREATE lambda expression [y => y.Right]
130+ var lambdaExpression = CreateLambdaExpression ( elementType , paths , currentIndex ) ;
131+
132+ // APPEND Method [.Select(y => y.Right)]
133+ var selectMethod = typeof ( Enumerable ) . GetMethods ( )
134+ . Single ( x => x . Name == "Select"
135+ && x . GetParameters ( ) . Length == 2
136+ && x . GetParameters ( ) [ 1 ] . ParameterType . GetGenericArguments ( ) . Length == 2 )
137+ . MakeGenericMethod ( elementType , lambdaExpression . Type . GetGenericArguments ( ) [ 1 ] ) ;
138+ expression = Expression . Call ( null , selectMethod , expression , lambdaExpression ) ;
139+
140+ return expression ;
141+ }
142+ }
143+ }
0 commit comments