I am playing with the TripPin OData demo from https://github.com/OData/ODataSamples/tree/master/Scenarios/TripPin/src/webapi using the OData C# client code generated by the OData template introduced by the following VS extension:
The C# code is simple:
var dc = new DefaultContainer(new Uri("http://canws212:23890/"));
dc.People.AddQueryOption("$expand", "Trips").Where(p => p.Trips.Any()).First();
This returns the first Person
entity having any Trip
child entities in the Trips
property.
But now I wish to get the first Person
entity having more than one Trip
child:
dc.People.AddQueryOption("$expand", "Trips").Where(p => p.Trips.Count() > 1).First();
This does not work. The request/response as captured by fiddler are:
Request
GET http://canws212:23890/People?$filter=Trips/$count%20gt%201&$top=1&$expand=Trips HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft ADO.NET Data Services
Host: canws212:23890
Connection: Keep-Alive
Response
HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
OData-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRGF5Zm9yY2VcU291cmNlXFJlcG9zXE9EYXRhU2FtcGxlc1xTY2VuYXJpb3NcVHJpcFBpblxzcmNcd2ViYXBpXE9EYXRhU2FtcGxlcy5XZWJBcGlTZXJ2aWNlXFBlb3BsZQ==?=
X-Powered-By: ASP.NET
Date: Sat, 23 Jul 2016 01:23:41 GMT
Content-Length: 1604
{
"error":{
"code":"","message":"The query specified in the URI is not valid. The method or operation is not implemented.","innererror":{
"message":"The method or operation is not implemented.","type":"System.NotImplementedException","stacktrace":" at Microsoft.OData.Core.UriParser.Visitors.QueryNodeVisitor`1.Visit(CountNode nodeIn)\r\n at Microsoft.OData.Core.UriParser.Semantic.CountNode.Accept[T](QueryNodeVisitor`1 visitor)\r\n at System.Web.OData.Query.ParameterAliasNodeTranslator.Visit(BinaryOperatorNode nodeIn)\r\n at Microsoft.OData.Core.UriParser.Semantic.BinaryOperatorNode.Accept[T](QueryNodeVisitor`1 visitor)\r\n at System.Web.OData.Query.FilterQueryOption.get_FilterClause()\r\n at System.Web.OData.Query.Validators.FilterQueryValidator.Validate(FilterQueryOption filterQueryOption, ODataValidationSettings settings)\r\n at System.Web.OData.Query.FilterQueryOption.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.OData.Query.Validators.ODataQueryValidator.Validate(ODataQueryOptions options, ODataValidationSettings validationSettings)\r\n at System.Web.OData.Query.ODataQueryOptions.Validate(ODataValidationSettings validationSettings)\r\n at System.Web.OData.EnableQueryAttribute.ValidateQuery(HttpRequestMessage request, ODataQueryOptions queryOptions)\r\n at System.Web.OData.EnableQueryAttribute.ExecuteQuery(Object response, HttpRequestMessage request, HttpActionDescriptor actionDescriptor)\r\n at System.Web.OData.EnableQueryAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)"
}
}
}
What is wrong? Is it a defect of the sample TripPin application or am I doing something wrong? How do I fix it?
EDIT
I would like the filtration to occur on the database itself. I.e. situation where first all the Person entities are fetched and then filtered out is far from ideal.
When you run the c# code
dc.People.AddQueryOption("$expand", "Trips").Where(p => p.Trips.Count() > 1).First();
Odata.Client API convert the query to URL.
The generated url in your case is:
http://canws212:23890/People?$filter=Trips/$count gt 1&$top=1&$expand=Trips
which doesn't match the URL Convention of OData V4
$filter is applied only on the properties of class (People) , and the class People can't navigate to the class Trip using the $filter keyword and there is no property count.
You expand Trips by the keyword Expand
to read more about OData Version 4.0. URL Conventions :
http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html
so, you get the error message
Edit2:
To get the first Person entity having more than one Trip
(I test it with url: http://services.odata.org/V4/TripPinServiceRW )
var dc = new DefaultContainer(new Uri("http://canws212:23890"));
var firstPerson = dc.People.AddQueryOption("$expand", "Trips")
.ToList()
.FirstOrDefault(p => p.Trips.Count > 1);
Console.WriteLine("{0} have: {1} trip", firstPerson.FirstName, firstPerson.Trips.Count);
//The result: Russell have: 3 trip
Edit: 3 if you review example 28, in the url:
http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html
Example 28: entity count in a $filter expression.
http://host/service/Categories?$filter=Products/$count gt 0
you find that V4 support count property so ,MY Conclusion
TripPin Demo site doesn't comply with URL Convention V4
and your query is valid , but can't be executed(for no count support)
http://canws212:23890/People?$filter=Trips/$count gt 1&$top=1&$expand=Trips