@@ -86,36 +86,7 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
8686 // {1}
8787 var right = Expression . Constant ( convertedValue , property . PropertyType ) ;
8888
89- Expression body ;
90- switch ( filterQuery . FilterOperation )
91- {
92- case FilterOperations . eq :
93- // {model.Id == 1}
94- body = Expression . Equal ( left , right ) ;
95- break ;
96- case FilterOperations . lt :
97- // {model.Id < 1}
98- body = Expression . LessThan ( left , right ) ;
99- break ;
100- case FilterOperations . gt :
101- // {model.Id > 1}
102- body = Expression . GreaterThan ( left , right ) ;
103- break ;
104- case FilterOperations . le :
105- // {model.Id <= 1}
106- body = Expression . LessThanOrEqual ( left , right ) ;
107- break ;
108- case FilterOperations . ge :
109- // {model.Id <= 1}
110- body = Expression . GreaterThanOrEqual ( left , right ) ;
111- break ;
112- case FilterOperations . like :
113- // {model.Id <= 1}
114- body = Expression . Call ( left , "Contains" , null , right ) ;
115- break ;
116- default :
117- throw new JsonApiException ( "500" , $ "Unknown filter operation { filterQuery . FilterOperation } ") ;
118- }
89+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
11990
12091 var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
12192
@@ -126,27 +97,109 @@ public static IQueryable<TSource> Filter<TSource>(this IQueryable<TSource> sourc
12697 throw new JsonApiException ( "400" , $ "Could not cast { filterQuery . PropertyValue } to { property . PropertyType . Name } ") ;
12798 }
12899 }
100+
101+ public static IQueryable < TSource > Filter < TSource > ( this IQueryable < TSource > source , RelatedAttrFilterQuery filterQuery )
102+ {
103+ if ( filterQuery == null )
104+ return source ;
105+
106+ var concreteType = typeof ( TSource ) ;
107+ var relation = concreteType . GetProperty ( filterQuery . FilteredRelationship . InternalRelationshipName ) ;
108+ if ( relation == null )
109+ throw new ArgumentException ( $ "'{ filterQuery . FilteredRelationship . InternalRelationshipName } ' is not a valid relationship of '{ concreteType } '") ;
110+
111+ var relatedType = filterQuery . FilteredRelationship . Type ;
112+ var relatedAttr = relatedType . GetProperty ( filterQuery . FilteredAttribute . InternalAttributeName ) ;
113+ if ( relatedAttr == null )
114+ throw new ArgumentException ( $ "'{ filterQuery . FilteredAttribute . InternalAttributeName } ' is not a valid attribute of '{ filterQuery . FilteredRelationship . InternalRelationshipName } '") ;
115+
116+ try
117+ {
118+ // convert the incoming value to the target value type
119+ // "1" -> 1
120+ var convertedValue = TypeHelper . ConvertType ( filterQuery . PropertyValue , relatedAttr . PropertyType ) ;
121+ // {model}
122+ var parameter = Expression . Parameter ( concreteType , "model" ) ;
123+
124+ // {model.Relationship}
125+ var leftRelationship = Expression . PropertyOrField ( parameter , relation . Name ) ;
126+
127+ // {model.Relationship.Attr}
128+ var left = Expression . PropertyOrField ( leftRelationship , relatedAttr . Name ) ;
129+
130+ // {1}
131+ var right = Expression . Constant ( convertedValue , relatedAttr . PropertyType ) ;
132+
133+ var body = GetFilterExpressionLambda ( left , right , filterQuery . FilterOperation ) ;
134+
135+ var lambda = Expression . Lambda < Func < TSource , bool > > ( body , parameter ) ;
136+
137+ return source . Where ( lambda ) ;
138+ }
139+ catch ( FormatException )
140+ {
141+ throw new JsonApiException ( "400" , $ "Could not cast { filterQuery . PropertyValue } to { relatedAttr . PropertyType . Name } ") ;
142+ }
143+ }
144+
145+ private static Expression GetFilterExpressionLambda ( Expression left , Expression right , FilterOperations operation )
146+ {
147+ Expression body ;
148+ switch ( operation )
149+ {
150+ case FilterOperations . eq :
151+ // {model.Id == 1}
152+ body = Expression . Equal ( left , right ) ;
153+ break ;
154+ case FilterOperations . lt :
155+ // {model.Id < 1}
156+ body = Expression . LessThan ( left , right ) ;
157+ break ;
158+ case FilterOperations . gt :
159+ // {model.Id > 1}
160+ body = Expression . GreaterThan ( left , right ) ;
161+ break ;
162+ case FilterOperations . le :
163+ // {model.Id <= 1}
164+ body = Expression . LessThanOrEqual ( left , right ) ;
165+ break ;
166+ case FilterOperations . ge :
167+ // {model.Id <= 1}
168+ body = Expression . GreaterThanOrEqual ( left , right ) ;
169+ break ;
170+ case FilterOperations . like :
171+ // {model.Id <= 1}
172+ body = Expression . Call ( left , "Contains" , null , right ) ;
173+ break ;
174+ default :
175+ throw new JsonApiException ( "500" , $ "Unknown filter operation { operation } ") ;
176+ }
177+
178+ return body ;
179+ }
180+
181+
129182 public static IQueryable < TSource > Select < TSource > ( this IQueryable < TSource > source , IEnumerable < string > columns )
130183 {
131- if ( columns == null || columns . Count ( ) == 0 )
184+ if ( columns == null || columns . Count ( ) == 0 )
132185 return source ;
133186
134187 var sourceType = source . ElementType ;
135-
188+
136189 var resultType = typeof ( TSource ) ;
137190
138191 // {model}
139192 var parameter = Expression . Parameter ( sourceType , "model" ) ;
140-
193+
141194 var bindings = columns . Select ( column => Expression . Bind (
142195 resultType . GetProperty ( column ) , Expression . PropertyOrField ( parameter , column ) ) ) ;
143-
196+
144197 // { new Model () { Property = model.Property } }
145198 var body = Expression . MemberInit ( Expression . New ( resultType ) , bindings ) ;
146-
199+
147200 // { model => new TodoItem() { Property = model.Property } }
148201 var selector = Expression . Lambda ( body , parameter ) ;
149-
202+
150203 return source . Provider . CreateQuery < TSource > (
151204 Expression . Call ( typeof ( Queryable ) , "Select" , new Type [ ] { sourceType , resultType } ,
152205 source . Expression , Expression . Quote ( selector ) ) ) ;
0 commit comments