0
votes

I have searched far and wide and been completely unable to find any good samples or tutorials of how to call service operations using the ODataLib. I've seen several that tell me how to get entities and entity sets and that's great, but in order to call a service operation.

I'm almost positive that I need to use something other than an ODataEntry, ODataFeed or ODataNavigationLink object. I've opened up the library in JustDecompile and see classes like ODataAction and ODataFunction but can't figure out how to use them.

My needs are pretty simple: I need to call WCF Data Services 5.0 service operations and I need to do it using JSON.

I'm also aware that the upcoming release of the WCF Data Services Client library is going to support JSON but unfortunately I have to code this up like yesterday. Additionally, out of desperation I even tried implementing the RC version (gasp) using Mark Stafford's JSON light sample but didn't really understand how it works.

Any help or direction would be GREATLY appreciated! (Especially if this comes up on your screen, Mark!)

Thanks! J

2

2 Answers

2
votes

The choice between ODataLib and WCF Data Services (which depends on ODataLib) as a client often boils down to the need for control. If your use case is straightforward and you need the most common functionality, WCF DS is probably a good fit. If you need advanced functionality or precise control over how a payload is read, ODataLib may be the better match. All that said, Vitek has already covered conceptually how you would read a service operation using ODataLib.

WCF DS will make reading JSON Light much easier in version 5.1. I will blog about this sometime this week, but the sample you referenced is what you'll need to do for this RC. There is only one new call in the sample I put together - the call to context.Format.UseJson(Func<Uri,ModelResolverResult>). Let's talk about why this call is necessary first. OData (at least in the Microsoft world) plays nice with strong typing. $metadata is a good example of OData describing the data model it is working with. With an Atom payload or even a JSON Verbose payload, much of that type information was carried in the payload. With JSON Light the goal was to strip as much of that metadata out of "transport" payloads as possible.

If the metadata isn't available in-band, it must be made available out-of-band. This is the fundamental requirement behind the signature of the call to UseJson. In essence, whenever WCF DS needs metadata it will go look up the model for a given URI. If it can't find that model it will eventually dial up the metadata to full. We want to preempt that as it will bloat the payload. We can preempt it by telling WCF DS how to resolve the model ahead of time. This is what the majority of the sample is doing.

Walking through the sample logically (yes, I know there's a number of other optimizations that could have been made - the sample was mostly optimized for readability):

  • If the model hasn't been resolved in the past
    • Construct a new XmlReader for the call to EdmxReader.TryParse
    • Name some values for the out params in EdmxReader.TryParse
    • Invoke EdmxReader.TryParse
      • If the call succeeded, cache the parsed model in a local dictionary (parsing is an expensive operation right now)
      • If the call failed, put together the errors and throw
  • Grab the correct model from the cached models and return it in a ModelResolverResult wrapper

Hopefully that makes sense. If it doesn't, I'd love to hear why so that I can make the blog post clearer. And remember, this will get much simpler by the time we release these bits to production.

2
votes

ODataLib doesn't know (and is not supposed to know) that it's reading a response of a service operation. The only thing which matters if what payload kind you want to read.

It depends on what the service operation returns. If it returns a single entry then use ODataLib just like reading a single entry payloads (for example ~/Products(0)) since the wire format will be the same. If the service operation returns a collection of entities, then read it like a feed.

If the service operation returns a single primitive or complex value, you can use ODataMessageReader.ReadProperty. I know the name of the method is a bit misleading but that's again because a property payload (like ~/Products(0)/Name) and a service operation returning a primitive or complex type use the exact same payload format. In this case you should ignore the name of the property returned (it will likely be the name of the service operation).

If the service operation returns a collection of primitive or complex values, you can use the ODataMessageReader.CreateODataCollectionReader. That will return ODataCollectionReader reader which you use in a very similar way of ODataReader. The interesting things it reports are items of the collection in question (the API in this case is pretty easy to understand).

It should not matter whether you need to read ATOM or JSON, that's up to you, the API is the same.