WCF Web API Enabling OData query support

Topics: Web Api
Sep 6, 2011 at 1:16 PM

I followed WCF Web API with ASP.NET MVC 3. Everything works well and it supports some of the operators like

?$filter=Name eq 'Phil Haack'

?$top=4

But when I try other query like

$filter=substringof('Phil', Name)

OR

$filter=length(Name) gt 5

It gives following error

"The server encountered an error processing the request. See server logs for more details."

But same queries work well with other ODATA services like Netflix. Does WCF Web API supports only limited set of query options, or I am missing something?

Thanks in advance.

Sep 6, 2011 at 7:40 PM
Edited Sep 6, 2011 at 7:45 PM

A quick reflection of the code I've found the following code.  Doesn't look like there's support for all OData query options.

public IQueryable Translate()
    {
      this.queryParameters = QueryTranslator.GetQueryParameters(this.url);
      if (this.queryParameters == null || this.queryParameters.Count <= 0)
        return this.rootQueryable;
      this.ObtainSkipTopCounts();
      return this.ProcessSkipTop(this.ProcessOrderBy(this.ProcessFilter(this.rootQueryable)));
    }

    private void ObtainSkipTopCounts()
    {
      int count;
      if (QueryTranslator.ReadSkipOrTopArgument("$skip", this.queryParameters, out count))
      {
        this.skipCount = new int?(count);
        int? nullable = this.skipCount;
        if ((nullable.GetValueOrDefault() >= 0 ? 0 : (nullable.HasValue ? 1 : 0)) != 0)
          throw new InvalidOperationException("Exception: Bad Request, skip count cannot be a negative value");
      }
      if (!QueryTranslator.ReadSkipOrTopArgument("$top", this.queryParameters, out count))
        return;
      this.topCount = new int?(count);
      int? nullable1 = this.topCount;
      if ((nullable1.GetValueOrDefault() >= 0 ? 0 : (nullable1.HasValue ? 1 : 0)) != 0)
        throw new InvalidOperationException("Exception: Bad Request, top count cannot be a negative value");
    }

    private IQueryable ProcessSkipTop(IQueryable queryable)
    {
      Type elementType = queryable.ElementType;
      Expression expression = queryable.Expression;
      if (this.skipCount.HasValue)
        expression = (Expression) Expression.Call(typeof (Queryable), "Skip", new Type[1]
        {
          elementType
        }, new Expression[2]
        {
          expression,
          (Expression) Expression.Constant((object) this.skipCount)
        });
      if (this.topCount.HasValue)
        expression = (Expression) Expression.Call(typeof (Queryable), "Take", new Type[1]
        {
          elementType
        }, new Expression[2]
        {
          expression,
          (Expression) Expression.Constant((object) this.topCount)
        });
      return queryable.Provider.CreateQuery(expression);
    }

    private IQueryable ProcessOrderBy(IQueryable queryable)
    {
      Type elementType = queryable.ElementType;
      IQueryable queryable1 = queryable;
      ParameterExpression parameterForIt = Expression.Parameter(elementType, "element");
      List<OrderingExpression> list = (List<OrderingExpression>) null;
      string queryStringItem = QueryTranslator.GetQueryStringItem("$orderby", this.queryParameters);
      if (!string.IsNullOrEmpty(queryStringItem))
        list = (List<OrderingExpression>) new RequestQueryParser.ExpressionParser(parameterForIt, queryStringItem).ParseOrdering();
      if (this.topCount.HasValue || this.skipCount.HasValue)
      {
        foreach (PropertyInfo propertyInfo in elementType.GetProperties())
        {
          if (Enumerable.FirstOrDefault<KeyAttribute>(Enumerable.OfType<KeyAttribute>((IEnumerable) propertyInfo.GetCustomAttributes(true))) != null)
          {
            string name = propertyInfo.Name;
            PropertyInfo property = propertyInfo;
            Expression orderingExpression = (Expression) Expression.Property((Expression) parameterForIt, property);
            if (list == null)
              list = new List<OrderingExpression>();
            list.Add(new OrderingExpression(orderingExpression, true));
          }
        }
      }
      if (list != null)
      {
        this.orderingExpressions = new List<OrderingExpression>();
        foreach (OrderingExpression orderingExpression in list)
          this.orderingExpressions.Add(new OrderingExpression((Expression) Expression.Lambda(orderingExpression.Expression, new ParameterExpression[1]
          {
            parameterForIt
          }), orderingExpression.IsAscending));
        queryable1 = RequestQueryParser.OrderBy(queryable, this.orderingExpressions);
      }
      return queryable1;
    }

    private IQueryable ProcessFilter(IQueryable queryable)
    {
      Type elementType = queryable.ElementType;
      IQueryable queryable1 = queryable;
      string queryStringItem = QueryTranslator.GetQueryStringItem("$filter", this.queryParameters);
      if (!string.IsNullOrEmpty(queryStringItem))
        queryable1 = RequestQueryParser.Where(queryable, queryStringItem);
      return queryable1;
    }
Sep 17, 2011 at 11:23 AM

I wondered why LinqPad OData support didn't work but this makes it clear.