private Expression AppendOnFilterExpression(QueryExpressionContext context, IEdmEntitySet entitySet, IEdmEntityType entityType) { var expectedMethodName = ConventionBasedMethodNameFactory.GetEntitySetMethodName(entitySet, RestierPipelineState.Submit, RestierEntitySetOperation.Filter); var expectedMethod = targetApiType.GetQualifiedMethod(expectedMethodName); if (expectedMethod == null || (!expectedMethod.IsFamily && !expectedMethod.IsFamilyOrAssembly)) { if (expectedMethod != null) { Trace.WriteLine($"Restier Filter found '{expectedMethodName}' but it is unaccessible due to its protection level. Your method will not be called until you change it to 'protected internal'."); } else { var actualMethodName = expectedMethodName.Replace(entitySet.Name, entityType.Name); var actualMethod = targetApiType.GetQualifiedMethod(actualMethodName); if (actualMethod != null) { Trace.WriteLine($"BREAKING: Restier Filter expected'{expectedMethodName}' but found '{actualMethodName}'. Your method will not be called until you correct the method name."); } } return(null); } var parameter = expectedMethod.GetParameters().SingleOrDefault(); if (parameter == null || parameter.ParameterType != expectedMethod.ReturnType) { return(null); } object apiBase = null; if (!expectedMethod.IsStatic) { apiBase = context.QueryContext.Api; if (apiBase == null || !targetApiType.IsInstanceOfType(apiBase)) { return(null); } } // The LINQ expression built below has three cases // For navigation property, just add a where condition from OnFilter method // For collection property, will be like "Param_0.Prop.AsQueryable().Where(...)" // For collection property of derived type, will be like "Param_0.Prop.AsQueryable().Where(...).OfType()" var returnType = context.VisitedNode.Type.FindGenericType(typeof(IQueryable <>)); var enumerableQueryParameter = (object)context.VisitedNode; Type elementType = null; if (returnType == null) { // This means append for properties model reference var collectionType = context.VisitedNode.Type.FindGenericType(typeof(ICollection <>)); if (collectionType == null) { return(null); } elementType = collectionType.GetGenericArguments()[0]; returnType = typeof(IQueryable <>).MakeGenericType(elementType); enumerableQueryParameter = Expression.Call(ExpressionHelperMethods.QueryableAsQueryableGeneric.MakeGenericMethod(elementType), context.VisitedNode); } else { elementType = returnType.GetGenericArguments()[0]; } var queryType = typeof(EnumerableQuery <>).MakeGenericType(elementType); var query = Activator.CreateInstance(queryType, enumerableQueryParameter); if (!(expectedMethod.Invoke(apiBase, new object[] { query }) is IQueryable result)) { return(null); } if (expectedMethod.ReturnType == returnType) { if (result != query) { return(result.Expression); } } else { // This means calling onFilter against derived type and based type is returned // Need to convert back to derived type with OfType result = ExpressionHelpers.OfType(result, elementType); return(result.Expression); } return(null); }