WebAPI OData $ Skip normal IQueryable double

I have implemented a custom IQueryable that is exposed through the OData WebAPI endpoint. The Get () controller structure is pretty standard:

[EnableQuery(
    AllowedQueryOptions = AllowedQueryOptions.Count
                          | AllowedQueryOptions.Filter
                          | AllowedQueryOptions.OrderBy
                          | AllowedQueryOptions.Skip
                          | AllowedQueryOptions.Top)]
[ODataRoute]
public PageResult<Foo> Get(ODataQueryOptions<Foo> queryOptions)
{

    var bars = new QueryableData<Foo>(_provider);

    var result = ((IQueryable<Foo>)queryOptions
        .ApplyTo(bars,
            new ODataQuerySettings(new ODataQuerySettings { EnableConstantParameterization = false, EnsureStableOrdering = false }))).ToList();
    var count = _provider.Count;
    return new PageResult<Foo>(result, null, count);
}

      

The odd behavior I see is that after the PageResult is returned, OData $ Skip is applied in the query string. For example:

  • if the query string contains? $ top = 10 & $ skip = 10, no results will be returned.
  • if the query string contains the character & top = 12 & skip = 10, results (2) will be returned.

What I want to do is prevent frameworks from applying the Skip to my results method as the query provider already implements the skip. Is there an ODataQuerySettings that can be set to prevent this double use of whitespace?

EDIT: Upon further investigation, when I remove $ count = true from the query string, pass (and the top) function as expected. This leads me to believe that my approach to implementing $ count = true is wrong. From my debugging sessions it seems that when $ count = true in the query parameters, the query has an expression tree applied to it twice, once with a return type of long and then again without the countlong wrapping expression. I tried to get the count back on the first pass and then request the second pass correctly, but this causes the skip expression to be deferred. There seems to be something very fundamental that I am missing here.

+6


source to share


4 answers


While reading the list of Github issues I came across this post: OData PageResult method ignoring the count parameter when using the EnableQuery attribute # 159 . The problem appears to be a combination of the EnableQuery attribute and a parameterized Get constructor that takes ODataQueryOptions. Using both means that you will implement the query parameters of the constructor by applying query expressions, and then the framework will apply whatever it can filter in the direction of the applied attribute; so apply things like skip, top and orderby twice.



+6


source


Since I wanted to deal with this pass, but still need other EnableQueryAttribute functions, I looked and noticed that it was virtual. I created a derived class and then tried to override the ApplyQuery methods. Unfortunately ODataQueryOptions only had private sets on their properties, so I skewed them a bit. (It feels dirty, but hey ..)

public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
    var skipOption = new SkipQueryOption("0", queryOptions.Context);
    typeof(ODataQueryOptions).GetProperty("Skip").SetValue(queryOptions, skipOption, null);

    return base.ApplyQuery(queryable, queryOptions);
}

      



If the skip parameter is now 0, it does not apply it when constructing the answer and no more "double skipped blues".

+3


source


I found that if you are using swagger / swashbuckle deleting the attribute together will result in some OData parameters not being generated by swashbuckle. Creating your own attribute that overrides the ApplyQuery method without doing anything and just returning the original query works as expected.

    // OData framework EnableQuery attribute will apply query again, after we have already applied the query to the result set
    // (So For e.g. you will get Top and Skip applied again on your results that have already had top and skip applied
    // this is a workaround the disables client side queries until this is fixed.
    // https://github.com/OData/WebApi/issues/159
    public class EnableCustomQueryAttribute : EnableQueryAttribute
    {
        public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
        {
            return queryable;
        }
    }

      

0


source


Building on slamb2k's answer, a slightly more recent version:

public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
    var parser = typeof(ODataQueryOptions).GetField("_queryOptionParser", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(queryOptions) as ODataQueryOptionParser;
    typeof(ODataQueryOptions).GetProperty("Skip").SetValue(queryOptions, new SkipQueryOption("0", queryOptions.Context, parser), null);
    typeof(ODataQueryOptions).GetProperty("Top").SetValue(queryOptions, new TopQueryOption("0", queryOptions.Context, parser), null);
    typeof(ODataQueryOptions).GetProperty("OrderBy").SetValue(queryOptions, new OrderByQueryOption("0", queryOptions.Context, parser), null);

    return base.ApplyQuery(queryable, queryOptions);
}

      

0


source







All Articles