Yavor is a PM at Snowflake working on developer experience. Previously at Docker, Auth0, Hulu, and Microsoft Azure.
23 August 2010
Recently we have had a few customers ask us how WCF RIA Services integrates with the WCF Routing features shipped in .Net 4. I spent some time over the last week building a prototype and learning about the issues you encounter in this scenario.
The sample source code is available here for download. Please keep in mind that in order for the sample works, you need the AdventureWorksLT database deployed to your local SQL Server Express instance… and don’t forget to update the connection string in Web.config!
First, let’s go over the scenarios where a router may be needed:
In the DMZ scenario, the WCF router may be overkill (correct me if I’m wrong). The WCF router is content-based, which means can decode incoming messages and make decisions based on different filters. Unless you are using the content routing features, a simpler router such as IIS’ Application Request Routing will offer better performance and probably be easier to set up. Sandrino Di Mattia has a really good write-up about how to do this.
With this disclaimer out of the way, here is how I got RIA Services working with the WCF content-based router.
In order to offer optimal wire size, RIA Services uses WCF’s binary encoder combined with plain old XML (referred to as POX, in contrast to SOAP). In WCF terms that means the BinaryMessageEncodingBindingElement needs to be configured with MessageVersion.None. However if you try that yourself, you’ll notice that the current implementation of the encoder does not allow that. RIA Services uses a private version of the encoder. So in my sample, I provide my own implementation of the binary encoder, which supports plain XML. Here is the binding that the router needs to understand RIA Services traffic.
Binding binaryPoxBinding = new CustomBinding(
new RiaBinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement {
ManualAddressing = true,
MaxReceivedMessageSize = int.MaxValue
}
);
Domain services use HTTP GET requests by default on their queries. GET offers caching benefits over POST, which are particularly relevant in the case of domain services, where large data sets with static data may be retrieved frequently.
Unfortunately, I could not figure out a way to get the WCF router to successfully route a GET request. Speaking to the team who designed the router, it seems SOAP scenarios took precedence to REST scenarios when they did the work, and since SOAP does everything over POST, the other HTTP verbs all get turned to POSTs. This is unfortunate, however it is easy to tell the domain service to switch to using only POST:
QueryAttribute(HasSideEffects = true)]
public IQueryable GetCustomers()
Along the same lines as the previous issue, the WCF router will ignore anything that comes in the URI after the endpoint name. This is unfortunate, as domain services expect the name of the query to come in the URI. WCF extensibility comes to the rescue, and by plugging in a simple message inspector, we can correct the address of outgoing messages.
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
// This ensures that the router will correctly roundtrip URI query string parameters
object via;
if (request.Properties.TryGetValue("Via", out via))
{
string query = (via as Uri).AbsolutePath.Replace(_routerAddress.AbsolutePath, "");
UriBuilder builder = new UriBuilder(_clientAddress);
builder.Path += query;
request.Headers.To = builder.Uri;
}
return null;
}
These three fixes are included in the sample project. Once the fixes are applied, we can start taking advantage of the capabilities of the router. By default we plug in a filter that matches all incoming messages.
rc.FilterTable.Add(new MatchAllMessageFilter(), endpointList);
However nothing prevents us from using some more sophisticated filters such as the XPathMessageFilter. Using that filter, we could look for specific elements inside the domain service message and route to different services based on that.
Hope this is useful to folks out there!
-Yavor