Search code examples
c#odataodata-v4

ODATAv4 $expand doesn't work when selecting explicit object


This first GET works perfectly fine:

http://localhost/webservice/odata/MyStuff?$filter=Id eq 2&$expand=History

It returns an array of MyStuff and History is expanded.

However, if I try to get MyStuff with the key specified, History navigation property is not expanded.

http://localhost/webservice/odata/MyStuff(2)?$expand=History

I already have the HttpConfiguration configured as follows at startup

             HttpConfiguration
            .Filter()
            .Expand()
            .Select()
            .OrderBy()
            .MaxTop(null)
            .Count();

Solution

  • There are multiple angles to problems like this:

    1. The data itself
    2. The query used to obtain the data
    3. The response type of the controller method that serves the data
    4. EnableQueryAttribute configuration

    You've already demonstrated that the data exists, so we can discount the data itself but the remaining factors can be implemented in all sorts of combinations.

    You should update your question to show an example of your controller method that handles the GET by Key request

    If you are using an ORM like Entity Framework then the following simple controller shows a minimal implementation that should work:

    [OData.EnableQuery]
    public virtual IHttpActionResult Get([FromODataUri] TKey key, ODataQueryOptions<MyStuff> queryOptions)
    {
        return SingleResultAction(dbContext.MyStuff.Where(x => x.Id == 2));
    }
    

    There are of course many other ways, from a minimal standpoint, using SingleResultAction forces you to pass in a query and validates that it only returns a single result.

    As long as an IQueryable<> result is returned from your method, the EnableQueryAttribute will try to apply the queryOptions to your query automatically.

    If your result set is manually constructed, or is NOT returned as IQueryable<> or cannot be automatically chained then you will have to Apply the $expand query option manually.

    You might do this to allow only expansion on certain navigation properties, this next example prepares an item by aggressively loading all the History records into memory

    Not recommended, especially if your navigation property may have many rows and the user will not be expanding very frequently, but it works:

    [OData.EnableQuery]
    public virtual MyStuff Get([FromODataUri] TKey key, ODataQueryOptions<MyStuff> queryOptions)
    {
        var item = dbContext.MyStuff.Include(X => x.History).Single(x => x.Id == 2);
        return item;
    }
    

    This example will still allow EnableQueryAttribute to $expand and even to $sort and $filter the records in the expansion, however, because the entire available data has been loaded into memory, there is no way for the EnableQueryAttribute to load additional endpoints.

    in this last example, even though the History items were loaded into memory, the will only appear in the JSON response if the $expand=History query option is provided by the caller.

    There are many other scenarios, OP didn't provide their implementation code so its a ll speculative from here. I have deliberately NOT demonstrated how to manually Apply the QueryOptions to your data query, which you can do, but until you get the hang of it, I encourage you to stay closer to a minimal implementation, we get a lot OOTB with OData, usually enough to get by.