Search code examples
asp.net-web-apiodata

OData EDM model - auto-expanding a nested Entity Type


I'm using OData v4 and the models are configured with the EdmModel Builder for our .NET Web API.

I have two models defined like this:

public class Customer
{
    public int CustomerId { get; set; }
    public string CustomerName { get; set; }
}

public class Order
{
    public int OrderId { get; set; }
    public string Category { get; set; }
    public Customer OrderCustomer { get; set;}
}   

These models have corresponding controllers and are registered as follows:

builder.EntitySet<Customer>("Customers")
          .EntityType
          .HasKey(x => x.CustomerId);

builder.EntitySet<Order>("Orders")
          .EntityType
          .HasKey(x => x.OrderId)
          .Expand(
            maxDepth: 2,
            expandType: SelectExpandType.Automatic,
            properties: nameof(Order.OrderCustomer));

I'm able to make OData requests to both these endpoints as follows: /Customers/{Id} and /Orders/{Id}.

I'm expecting that when I query the Orders, the nested EntitySet Customers will auto expand since I've set expandType: SelectExpandType.Automatic on the Order EntitySet. However, I can't get the CustomerOrder property to auto-expand on the Customer and I have to call the request with an expand parameter:

/Orders/{Id}?$expand=OrderCustomer.

I think this is because both Customer and Order are registered as EntitySets, so OData expects that an expand parameter be provided if they are nested. Is there a way to get the OrderCustomer property to auto-expand (i.e. without the need for the expand parameter to be provided)? My understanding of OData/ Edm Models is pretty elementary so any help is appreciated.


Solution

  • Your fluent configuration is correct, for OData v4, that will work for both collection and item queries.

    If it is not working for you there are 3 possible issues:

    1. You do not appear to be using the OData v4 URL convention for item queries, in v4 the expected URL is:

      /Orders({Id})
      

      This brings into question how you modified the router to support the v3 syntax, there are multiple variation on how to implement the v3 routes so it is possible that changes made in this area could affect the way that default expansion and selection is applied, or if it should apply.

    2. You may not be including the navigation data in your data query. If the data is not retrieved from the data store, then it stands to reason that it will not be in the output recordset. If you are manually using the ODataQueryOptions.ApplyTo() to apply the user request to your query, then this will not take into account the configuration on the model, it will only apply thequery options that the caller has specified.

    3. The caller might be specifying an Empty $expand= which will cancel out the auto configuration. Even if the originating caller has not specified any query options it is common enough for OData APIs to have standard or custom middleware running that might be manipulating the request query strings. To verify the URL is untampered, log it in your GET method handler and make sure the $expand is not specified.

      • As with the previous point, the ODataQueryOptions parameter in your GET method should NOT show any value for the SelectExpand if you want the auto configuration to be applied.

    Finally, the last place to check is that you haven't overriden the default EnableQueryAttribute. If you have implemented your own custom implementation of EnableQueryAttribute then make sure that you still call the base implementation to correctly apply the ODataQueryOptions AND the schema defaults to the underlying IQueryable result.