Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OData v4 Web API 2.2 deep level expand not working

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

like image 735
Jerther Avatar asked Feb 12 '15 21:02

Jerther


1 Answers

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
            }]
        }]
    }]
}
like image 78
Brad Avatar answered Nov 19 '22 10:11

Brad