Items are counted in OData v4 WebAPI response

How to return the cardinality in a HTTP OData v4 response?

I need this number for pagination, so there should be a number of items after filtering but before "skip" and "top".

I have already tried passing parameters $ inlinecount = allpages and $ count = true in request parameters in url ( https://damienbod.wordpress.com/2014/06/13/web-api-and-odata-v4-queries-functions- and-attribute-routing-part-2 / - "Example $ count"), but my responses from WebAPI always only have query results (collection) - the whole response looks like this:

[
    {
        "Name":"name1", 
        "age":5
    }, 
    {
        "Name":"name2", 
        "age":15
    }
]

      

There is nothing like "odata.count" in the answer.

I also tried to return PageResult instead of IQueryable in my WebAPI controller action (as described here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query -options # server-paging ), but Request.GetInlineCount () is deprecated and is always null.

Any ideas?

[Update] I just found the same problem: WebApi with Odata NextPage and Count not showing up in the JSON response and I removed the [EnableQuery] attribute and now my response looks like this:

{
    "Items":
    [
        {
            "Name":"name1", 
            "age":5
        }, 
        {
            "Name":"name2", 
            "age":15
        }
    ],
    "NextPageLink":null,
    "Count":null
}

      

But still "Count" is always null. :(


Edit: After debugging and looking for the counter value in the Request properties in my controller, I found that the correct Count value is in a property named "System.Web.OData.TotalCount". So right now I am retrieving this value from this request property and my controller looks like this:
public PageResult<People> Get(ODataQueryOptions<People> queryOptions)
{
    var query = _context.People.OrderBy(x => x.SomeProperty);
    var queryResults = (IQueryable<People>)queryOptions.ApplyTo(query);
    long cnt = 0;
    if (queryOptions.Count != null)
        cnt = long.Parse(Request.Properties["System.Web.OData.TotalCount"].ToString());

    return new PageResult<People>(queryResults, null, cnt);
}

      

And it works great, but I still don't know why I have to use workarounds like this.

+3


source to share


4 answers


For future reference (OData v4):

It is primarily $inlinecount

not supported in OData v4

, so should be used instead $count=true

.

Secondly, if yours is normal ApiController

and you are returning a type IQueryable<T>

, then you can attach a property count

to the returned result:

using System.Web.OData;
using System.Web.OData.Query;
using System.Web.OData.Extensions;

//[EnableQuery] // -> If you enable globally queries does not require this decorator!
public IHttpActionResult Get(ODataQueryOptions<People> queryOptions)
{
    var query = _peopleService.GetAllAsQueryable(); //Abstracted from the implementation of db access. Just returns IQueryable<People>
    var queryResults = (IQueryable<People>)queryOptions.ApplyTo(query);
    return Ok(new PageResult<People>(queryResults, Request.ODataProperties().NextLink, Request.ODataProperties().TotalCount));
}

      

Note: OData functionality is not supported ApiController

, so you cannot have things like count

or $metadata

. If you choose, use the simple ApiController

path above, which you should use to return the property count

.


To fully support OData functionality, you must implement ODataController

as follows:

PeopleController.cs

using System.Web.OData;
using System.Web.OData.Query;

public class PeopleController : ODataController
{
    [EnableQuery(PageSize = 10, AllowedQueryOptions = AllowedQueryOptions.All)]
    public IHttpActionResult Get()
    {
        var res = _peopleService.GetAllAsQueryable();
        return Ok(res);
    }
}

      



App_Start \ WebApiConfig.cs

public static void ConfigureOData(HttpConfiguration config)
{
    //OData Models
    config.MapODataServiceRoute(routeName: "odata", routePrefix: null, model: GetEdmModel(), batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
    config.EnsureInitialized();
}

private static IEdmModel GetEdmModel()
{
    var builder = new ODataConventionModelBuilder
    {
        Namespace = "Api",
        ContainerName = "DefaultContainer"
    };
    builder.EntitySet<People>("People").EntityType.HasKey(item => item.Id); //I suppose the returning list have a primary key property(feel free to replace the Id key with your key like email or whatever)
    var edmModel = builder.GetEdmModel();
    return edmModel;
}

      

Then you access the OData Api this way (example):

encoded uri:

http://localhost:<portnumber>/People/?%24count=true&%24skip=1&%24top=3

      

decoded:

http://localhost:<portnumber>/People/?$count=true&$skip=1&$top=3

      

Literature:

+5


source


What I am using with oData v4:



Request.ODataProperties().NextLink, 

Request.ODataProperties().TotalCount

      

+2


source


Could you take a look at the sample TripPin web api service at https://github.com/OData/ODataSamples/blob/master/Scenarios/TripPin . Can you follow the code in Airport Controller and service with code http://services.odata.org/TripPinWebApiService/Airports ? $ Count = true can return the count correctly.

0


source


This can also be achieved with an action filter:

/// <summary>
/// Use this attribute whenever total number of records needs to be returned in the response in order to perform paging related operations at client side.
/// </summary>
public class PagedResultAttribute: ActionFilterAttribute
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="actionExecutedContext"></param>
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        base.OnActionExecuted(actionExecutedContext);
        if (actionExecutedContext.Response != null)
        {                
            dynamic responseContent=null;
            if (actionExecutedContext.Response.Content != null)
                responseContent = actionExecutedContext.Response.Content.ReadAsAsync<dynamic>().Result;
            var count = actionExecutedContext.Response.RequestMessage.ODataProperties().TotalCount;
            var res = new PageResult<dynamic>() {TotalCount=count,Items= responseContent };

            HttpResponseMessage message = new HttpResponseMessage();
            message.StatusCode = actionExecutedContext.Response.StatusCode;

            var strMessage = new StringContent(JsonConvert.SerializeObject(res), Encoding.UTF8, "application/json");
            message.Content = strMessage;
            actionExecutedContext.Response = message;               
        }           
    }
}

      

And the custom PageResult class:

public class PageResult<T>
{      
    public long? TotalCount { get; set; }
    public T Items { get; set; }
}

      

Using:

[PagedResult]
[EnableQuery()]  

      

0


source







All Articles