The situation
I'm trying to expand "Item" to three levels:
Item.Product.Model.Type
So I call this nested query options url:
http://xxx/api/Items?$expand=Product($expand=Model($expand=Type))
I Get a warning that the max depth of 2 has been reached so I set the suggested MaxExpansionDepth attribute to 3. But then, the "Type" property is not returned! This is covered by this SO question
Then I look at the official OData V4 standard and it says I should use slashes in the expand option, like so:
http://xxx/api/Items?$expand=Product/Model/Type
But that gives me an error telling:
The query specified in the URI is not valid. Found a path traversing multiple navigation properties. Please rephrase the query such that each expand path contains only type segments and navigation properties.
Which this SO answer covers but the answer is contradictory to the official OData doc. What does that even mean anyway.
The question
What is the official, standard and working way of using the $expand query option for deep levels with OData v4 and Web API 2.2
After working with Jerther in chat, we narrowed the problem down to the expanded properties not being marked as contained navigations. As a result, the OData framework was removing them since they did not have corresponding entity sets defined. Updating the model to specifically declare the containment appears to have solved the problem.
Containment can be specified in a couple of ways, depending on what model builder is being used. In the case of the ODataConventionModelBuilder
you can add the System.Web.OData.Builder.ContainedAttribute
to the property in question, while for the ODataModelBuilder
you can use the ContainsMany<T>
method on the EntityTypeConfiguration
instance for the containing class.
Also, at the moment, a cascaded expand will stop where a complex type contains an Entity type.
UPDATE:
Defining all types in the chain as EntitySet works.
builder.EntitySet<Item>("Items");
builder.EntitySet<Product>("Products");
builder.EntitySet<Model>("Models");
builder.EntitySet<Type>("Types");
It seems defining them as EntityType
isn't sufficient.
see here: https://github.com/OData/WebApi/issues/226
Original Answer
I tried reproing your situation and couldn't. Is it possible that "Types" isn't being set in your action? Here was my little repro
public class ItemsController : ODataController
{
[HttpGet]
[EnableQuery(MaxExpansionDepth = 10)]
[ODataRoute("/Items")]
public IHttpActionResult GetItems()
{
return this.Ok(CreateItem());
}
private Item CreateItem()
{
return new Item
{
Id = 1,
Products = new Product[]
{
new Product
{
Id = 2,
Models = new Model[]
{
new Model
{
Id = 3,
Types = new MyType[]
{
new MyType
{
Id = 4,
},
},
},
},
},
},
};
}
}
Which when called with /Items?$expand=Products($expand=Models($expand=Types)) resulted in the following:
{
"@odata.context": "http://localhost:9001/$metadata#Items/$entity",
"Id": 1,
"[email protected]": "http://localhost:9001/$metadata#Items(1)/Products",
"Products": [{
"Id": 2,
"[email protected]": "http://localhost:9001/$metadata#Items(1)/Products(2)/Models",
"Models": [{
"Id": 3,
"[email protected]": "http://localhost:9001/$metadata#Items(1)/Products(2)/Models(3)/Types",
"Types": [{
"Id": 4
}]
}]
}]
}
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