Tuesday, August 10, 2010

Dynamically Computing Web Service Addresses for Azure-Silverlight Applications

If you work with Silverlight or with Windows Azure you can experience some pain when it comes to getting your WCF web services to work at first. If you happen to be using both Silverlight and Azure together you might feel this even more.

One troublesome area is the Silverlight client knowing the address of its Azure web service. Let’s assume for this discussion that your web service is a WCF service that you’ve defined in a .svc file (“MyService.svc”) that resides in the same web role that hosts your Silverlight application. What’s the address of this service? There are multiple answers depending on the context you’re running from:
  1. If you’re running locally, your application is running under the Dev Fabric (local cloud simulator), and likely has an address similar to http://127.0.0.1:81/MyService.svc.
  2. If you run the web project outside of Windows Azure, it will run under the Visual Studio web server and have a different address of the form http://localhost:<port>/MyService.svc.
  3. If you deploy your application to a Windows Azure project’s Staging slot, it will have an address of the form http://<guid>.cloudapp.net/MyService.svc. Moreover, the generated GUID part of the address changes each time you re-deploy.
  4. If you deploy your application to a Windows Azure project’s Production slot, it will have the chosen production address for your cloud service of the form http://<project>.cloudapp.net/MyService.svc.
One additional consideration is that when you perform an Add Service Reference to your Silverlight project the generated client configuration will have a specific address. All of this adds up to a lot of trouble each time you deploy to a different environment. Fun this is not.

Is there anything you can do to make life easier? There is. With a small amount of code you can dynamically determine the address your Silverlight client was invoked at, and from there you can derive the address your service lives at.

You can use code similar to what’s shown below to dynamically compute the address of your service. To do so, you’ll need to make some replacements to the MyService = … statement:
1. Replace “MyService.MyServiceClient” with the namespace and class name of your generated service proxy client class.
2. Replace “CustomBinding_MyService” with the binding name used in the generated client configuration file for your service endpoint (ServiceReferences.ClientConfig).
3. Replace “MyService.svc” with the name of your service .svc file.

string hostUri = System.Windows.Browser.HtmlPage.Document.DocumentUri.AbsoluteUri;
int pos = hostUri.LastIndexOf('/');
if (pos != -1)
{
    hostUri = hostUri.Substring(0, pos);
}
MyService.MyServiceClient MyService = new MyService.MyServiceClient("CustomBinding_MyService", hostUri + "/MyService.svc");

With this in place you can effortlessly move your application between environments without having to change addresses in configuration files. Note this is specific to “traditional” WCF-based web services only. If you’re working with RIA Services, the client seems to know its service address innately and you should not need to worry about computing web service addresses.

2 comments:

Steve Marx said...

Why can't you just use a relative address?

David Pallmann said...

I don't believe relative web service addresses have worked historically with Silverlight. In Silverlight 4 there is relative address support, but even there it's somewhat tricky as the address is relative to the XAP ClientBin location so you may find yourself doing some "../" prefixing to get to where your .svc file is. Tim Heuer's post on this (and the subsequent discussions) are useful http://timheuer.com/blog/archive/2010/04/05/managing-service-references-in-silverlight-applications-for-different-environments.aspx