[Tutorial & Sample] Client Delayed Query
In OData Client 6.5.0, together with OData Client Code Generator 2.0.0, we have improved the user experience on the client side by introducing delayed query into it. This feature enables valid compositions as many as you want when building a query in client, getting all parts into one request URL and sending it out when you get it ready.
Currently, we support the generation of all operation imports and operations bound on single entity or a collection of entities in OData Client Code Generator 2.0.0, so as to invoke them directly in C# or VB .NET when writing client code to build queries. Bound functions can be used in query options such as “$filter” or “$orderby” too. Operations bound to other resources (e.g. primitive types, complex types, etc.) are not supported yet. Neither are unbound operations exclusive of operation imports.
Basically, we aligned with the previous code experience in OData client, but there’re still some minor changes have been made to differentiate a single entity and a collection of entities when building queries. Plus, some new features together with new code patterns will be introduced here.
Hints at the beginning:
1. All samples below are based on the service EDM model here.
2. You can read “How to use OData Client Code Generator to generate client-side proxy class” for generating client side proxy class.
Now, let’s start!
Query a Singleton or a Single Entity
As you know, we simply treated Singletons the same as EntitySets before, which means you cannot distinguish a Singleton from an EntitySet without looking them up in the metadata document. It has been optimized by using another type generated by T4 template to represent Singletons, while EntitySets stay unchanged.
An entity selected from a collection of entities is also represented by this new type when building queries. Use the “ByKey” method to get the specific entity by passing a dictionary as the argument into it to specify its key(s). The usages of Projection and Expand are the same as what they’re on EntitySet. To retrieve the item from the server after query building is finished, call “GetValue” to send the request out and get the result back.
> Query a Singleton:
> Query a person whose UserName is “russellwhyte”:
> Projection and Expand:
Query Navigation Properties
To get a navigation property, we can only use “Expand” on an entity before. This has extremely limited our query capability on the client side. Things have changed now. We can get only the navigation properties of an entity without “Expand”, and invoke operations on them conveniently.
> Query a navigation property on an entity:
> Query a navigation property on an entity for multiple levels:
ActionImports and actions bound to an EntityType or a collection of entities are supported now. Action requests should be sent with the “POST” method, with all parameters (if any) serialized into the body. Actions cannot be further composed, so they’re always the termination of a query. Actions may or may not have return values. Please call “GetValue” for a single result and “Execute” or “foreach” to iterate directly for a collection results. If the action has no return values, please call “Execute” to send the request out.
> Invoke an ActionImport:
> Invoke Action bound on a collection of entities
> Invoke Action bound on an EntityType:
> Invoke bound Action on a retrieved entity:
FunctionImports, functions bound to an EntityType or a collection of entities are supported now. The parameters (if any) of a function will be serialized into its request URI as inline parameters. Functions can be composable if they’re marked with the attribute “IsComposable” as “true” in the metadata, then they can be further composed in a query. If an uncomposable function is attached with more segments, an exception will be thrown. Functions must have return values, please call “GetValue” for single result and “Execute” or “foreach” to iterate directly for collection results.
> Invoke FunctionImport returning a single result with inline parameters:
> Invoke Function bound on a collection of entities
> Invoke Function bound on EntityType returning a collection result:
> Invoke bound Function on a retrieved entity:
> Invoke Function in query options:
Use “GetValue” to get the real type of a function return value under synchronous circumstances, then you can get access to its properties or whatever if you want. In Async environment, there’s no “GetValue” method provided, so please substitute it with “GetValueAsync().Result”. Don’t worry, neither “GetValue” nor “GetValueAsync().Result” will send the partial request out when they’re using inside LINQ query expression.
> Invoke Function with further composition:
In OData, we can add a type segment to an entity resource in order to cast it into its derived type. If you want to cast an entity, there’re all “CastTo[DerivedType]” methods respectively generated on it to accomplish this. If it is a collection of entities and you want to get all of the derived type from it, “OfType<T>” will be the best choice.
> Add type segment to a collection
> Add type segment to an entity