What are the Request and Response Pipeline configurations in WCF Data Services Client?

In WCF Data Services 5.4 we added a new pattern to allow developers to hook into the client request and response pipelines. In the server, we have long had the concept of a processing pipeline. Developers can use the processing pipeline event to tweak how the server processes requests and responses. This concept has now been added to the client (though not as an event). The feature is exposed through the Configurations property on the DataServiceContext. On Configurations there are two properties, called ResponsePipeline and RequestPipeline. The ResponsePipeline contains configuration callbacks that influence reading to OData and materializing the results to CLR objects. The RequestPipeline contains configuration callbacks that influence the writing of CLR objects to the wire. Developers can then build on top of the new public API and compose higher level functionality.

The explanation might be a bit abstract so let’s move look at a real world example. The code below will document how to remove a property that is unnecessary on the client or that causes materialization issues. Previously this was difficult to do, and impossible if the payload was returning the newer JSON format, but this scenario is now trivially possible with the new hooks. Below is a code snippet to remove a specific property:

This code is using the OnEntryRead response configuration method to remove the property. Behind the scenes what is happening is the Microsoft.Data.ODataReader calls reader.Read(). As it reads though the items, depending on the ODataItem type a call will be made to the all configuration callbacks of that type that are registered. A couple notes about this code:

  1. Since ODataEntry.Properties is an IEnumerable<ODataProperty> and not an ICollection<ODataProperty>, we need to replace the entire IEnumerable instead of just calling ODataEntries.Properties.Remove().
  2. ResolveType is used here to use the TypeName and get the EntityType, typically for a code generated DataServiceContext this method is automatically hooked up but if you are using a DataServiceContext directly then delegate code will need to be written.

What if this scenario has to occur for other properties on the same type or properties on a different type? Let’s make some changes to make this code a bit more reusable.

Extension method for removing a property from an ODataEntry:

Extension method for removing a property from the ODataEntry on the selected type:

And now finally the code that the developer would write to invoke the method above and set the configuration up:

The original code is now broken down and is more reusable. Developers can use the RemoveProperties extension above to remove any property from a type that is in the ODataEntry payload. These extension methods can also be chained together.

The example above shows how to use OnEntryEnded, but there are a number of other callbacks that can be used. Here is a complete list of configuration callbacks on the response pipeline:

All of the configuration callbacks above with the exception of OnEntityMaterialized and OnMessageReaderSettingsCreate are called when the ODataReader is reading through the feed or entry. The OnMessageReaderSettingsCreate callback is called just prior to when the ODataMessageWriter is created and before any of the other callbacks are called. The OnEntityMaterialized is called after a new entity has been converted from the given ODataEntry. The callback allows developers to apply any fix-ups to an entity after it was converted.

Now let’s move on to a sample where we use a configuration on the RequestPipeline to skip writing a property to the wire. Below is an example of an extension method that can remove the specified properties before it is written out:

As you can see we are following the same pattern as the extension method we wrote to RemoveProperties for the ResponsePipeline. In comparison to this extension method this function doesn’t require the type resolving func, so it’s a bit simpler. The type information is specified on the OnEntryEnding args in the Entity property. Again this example only touches on ODataEntryEnding. Below is the complete list of configuration callbacks that can be used:

With the exception of OnMessageWriterSettingsCreated, the other configuration callbacks are called when the ODataWriter is writing information to the wire.

In conclusion, the request and response pipelines offer ways to configure the how payloads are read and written to the wire. Let us know any other questions you might have to leverage this feature.

Chris Robinson – OData Team