I'm useing EntitySetController to create an oData web api controller, and everything works well, except to get the total records count.
THe controller is defined as below:
public class MyODataController : EntitySetController<Entity1, int> where TEntity : class
{
public override IQueryable<Entity1> Get()
{
return EntityDatabase.Get();
}
}
when I call the count through:
http://localhost:44789/oData/MyOData/$count
I get the error: Invalid action detected. '$count' is not an action that can bind to 'Collection([Entity1 Nullable=False])'.
Unfortunately, Web API doesn't support $count out-of-the box quite yet although it should in a future version. In the meantime, you can still add support by defining these classes:
public class CountODataRoutingConvention : EntitySetRoutingConvention
{
public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
{
if (controllerContext.Request.Method == HttpMethod.Get && odataPath.PathTemplate == "~/entityset/$count")
{
if (actionMap.Contains("GetCount"))
{
return "GetCount";
}
}
return null;
}
}
public class CountODataPathHandler : DefaultODataPathHandler
{
protected override ODataPathSegment ParseAtEntityCollection(IEdmModel model, ODataPathSegment previous, IEdmType previousEdmType, string segment)
{
if (segment == "$count")
{
return new CountPathSegment();
}
return base.ParseAtEntityCollection(model, previous, previousEdmType, segment);
}
}
public class CountPathSegment : ODataPathSegment
{
public override string SegmentKind
{
get
{
return "$count";
}
}
public override IEdmType GetEdmType(IEdmType previousEdmType)
{
return EdmCoreModel.Instance.FindDeclaredType("Edm.Int32");
}
public override IEdmEntitySet GetEntitySet(IEdmEntitySet previousEntitySet)
{
return previousEntitySet;
}
public override string ToString()
{
return "$count";
}
}
Registering them in MapODataRoute:
IList<IODataRoutingConvention> routingConventions = ODataRoutingConventions.CreateDefault();
routingConventions.Insert(0, new CountODataRoutingConvention());
config.Routes.MapODataRoute("OData", "odata", GetModel(), new CountODataPathHandler(), routingConventions);
And in your controller, adding this method:
public HttpResponseMessage GetCount(ODataQueryOptions<TEntity> queryOptions)
{
IQueryable<TEntity> queryResults = queryOptions.ApplyTo(Get()) as IQueryable<TEntity>;
int count = queryResults.Count();
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(count.ToString(), Encoding.UTF8, "text/plain");
return response;
}
To avoid having to copy GetCount() to every controller, you could define a base class that derives from EntitySetController that defines GetCount.
If you use the following:
http://localhost:44789/oData/MyOData?$inlinecount=allpages
then the total count will be included in your get return.
The latest package Web API 2.2 for OData v4.0 has $count support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With