Reputation: 3484
In an OData 4 service on Web API, what is the proper way to call nested $expand from a .NET client? We are using the OData Client Generator. Back in the prior WCF Data Services with OData 3 service we could call .Expand("Customers/Orders")
. In Web API with OData 4 we can no longer do so and receive the following should you attempt .Expand("Customers/Orders")
:
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.
We are able to work around this by calling expand like so: .Expand("Customers($expand=Orders)")
. In non-nested $expand scenarios, I like the lambda support like so .Expand(d => d.Customers)
. Is there a proper way in .NET OData 4 client to call nested expands without the unfortunate magic string of .Expand("Customers($expand=Orders)")
? If not, is there a cleaner string pattern like "Customers/Orders" that would work? Thanks.
Upvotes: 12
Views: 8313
Reputation: 403
A little extension for that :
public static DataServiceQuery<TSource> Expand<TSource,TNavigation,TExpand>(this DataServiceQuery<TSource> dataServiceQuery, Expression<Func<TSource, DataServiceCollection<TNavigation>>> expression, Expression<Func<TNavigation,TExpand>> navigation)
{
var expressionName = (expression.Body as System.Linq.Expressions.MemberExpression).Member.Name;
var navigationName = (navigation.Body as System.Linq.Expressions.MemberExpression).Member.Name;
return dataServiceQuery.Expand($"{expressionName}($expand={navigationName})");
}
Now you have intellisense and type checking
Example :
db.Container.Expand(c=> c.Customers, customer => customer.Orders)
Upvotes: 3
Reputation: 3347
In OData v4, it is not valid to expand multi levels, such as what you mentioned in the question: .Expand("Customers/Orders"). I dont think the client will support such API. Here is what in the ABNF http://docs.oasis-open.org/odata/odata/v4.0/os/abnf/odata-abnf-construction-rules.txt:
expand = '$expand' EQ expandItem *( COMMA expandItem )
expandItem = STAR [ ref / OPEN levels CLOSE ]
/ expandPath
[ ref [ OPEN expandRefOption *( SEMI expandRefOption ) CLOSE ]
/ count [ OPEN expandCountOption *( SEMI expandCountOption ) CLOSE ]
/ OPEN expandOption *( SEMI expandOption ) CLOSE
]
expandPath = [ qualifiedEntityTypeName "/" ]
*( ( complexProperty / complexColProperty ) "/" [ qualifiedComplexTypeName "/" ] )
navigationProperty
[ "/" qualifiedEntityTypeName ]
Upvotes: 1
Reputation: 3014
The request that you want to send is:
GET http://host/service/Customers/Orders
right?
According to the OData protocol:
To request related entities according to a particular relationship, the client issues a GET request to the source entity’s request URL, followed by a forward slash and the name of the navigation property representing the relationship.
So such request is not supported as the "Customers" before "/Orders" is the name of an entity set instead of a single entity. You can only write nested expanding like:
GET http://host/service/Customers(1)/Orders
Which corresponds to the following code snippets using OData V4 Code Generator:
var orders = context.Customers.ByKey(new Dictionary<string, object>() { { "ID", 1 } }).Orders.Execute();
And you need to iterate through all the customers to get all their orders.
Upvotes: 3