Can Web API expose weakly typed queryables?

Topics: ASP.NET Web API
Jun 25, 2012 at 8:09 PM

Hello everyone,

Is it possible to remove the reflection checks on [Queryable] actions? I've created a custom IQueryable and IQueryProvider that will handle everything being passed to the filter, but I keep hitting the following error right after my IQueryable's ElementType gets requested:

The query specified in the URI is not valid. Parse error in $filter. No property or field 'Name2' exists in type 'Product' (at index 11)

In WCF Data Services you can get around it by using an IDataServiceMetadataProvider but it looks like Web API is always strongly typed. (I hope i'm wrong :) )

 

Any help would be greatly appreciated!

Jun 25, 2012 at 8:22 PM

Right, Queryable is strongly typed and unfortunately there is no public extension point to override it. Could you please let me know the scenario that you are trying to make work so that I can suggest a workaround if any instead ?

Jun 25, 2012 at 8:55 PM
raghuramn wrote:

Right, Queryable is strongly typed and unfortunately there is no public extension point to override it. Could you please let me know the scenario that you are trying to make work so that I can suggest a workaround if any instead ?

Hey Raghuramn, thanks for the swift reply.

What I'm trying to do is expose some product data in xml/json that's easy to filter by anyone. (mostly using jQuery) The problem is that not all of my products are of the same type. I'm required to permit a query such as: /API/Products?$filter=ShoeSize eq 10, returning only products of the types that have a ShoeSize property. I've made this work already using a custom IQueryable. Web API just wont allow me to call it because the base type doesn't have the properties I query for.

I've also thought about parsing the OData filter manually and build the results based on that, but that seems both time consuming and error prone. 3rd parties are supposed to be consuming the service and experience taught me that rolling your own solution or deviating from the standard will come back to haunt you :)

Jun 29, 2012 at 1:38 AM

you should be able to write an action filter that runs before the QueryableFilter or inherits QueryableFilter and replaces response.Content to be of type IQueryable<T> where T has that particular property. Something like,

public class RestrictingQueryableAttribute : QueryableAttribute
    {
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            HttpRequestMessage request = actionExecutedContext.Request;
            HttpResponseMessage response = actionExecutedContext.Response;

            IQueryable<Product> query;
            if (response != null && response.TryGetContentValue(out query))
            {
                string filter = request.RequestUri.ParseQueryString()["$filter"];

                if (filter.Contains("ShoeSize"))
                {
                    ((ObjectContent)response.Content).Value = query.OfType<Shoe>();
                }
                else if (filter.Contains("Calories"))
                {
                    ((ObjectContent)response.Content).Value = query.OfType<Food>();
                }
            }

            base.OnActionExecuted(actionExecutedContext);
        }
    }

The tricky part is the one where you need to look into the $filter string to figure out what all properties are being accessed and is certainly not robust here in this example. Let me know if it works.

Jul 4, 2012 at 3:18 PM

I'll try out the method you proposed, thanks a lot for the suggestion :)