Calling WCF services from a Silverlight 2 app.
As far as I am concerned Silverlight 2 is pretty cool and should have been developed years ago
. Well guess that isn't the case but it is here now.
One thing I like about Silverlight is how easy it is to call a web service. I guess that is a must be because when developing a LOB application in Silverlight you are going to be calling your server to get and store data. So calling the back end is as easy as adding a new Silverlight-enabled WCF Service, implementing the ServiceContract and setting a service reference from your Silverlight project. Things will work immediately and you are all good to go.
Or maybe not 
Well your service will work just fine in your development machine, no problems there. But when you move your project to another machine thing might just stop working and you will see a ProtocolException with message "The remote server returned an unexpected response: (404) Not Found.".
So what gives?
Well the problem is that the WCF stores the absolute Uri for the WCF service in its configuration file, the ServiceReferences.ClientConfig. In this case the configuration looks like this:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_Service" maxBufferSize="65536"
maxReceivedMessageSize="65536">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:4370/SilverlightApplication3Web/Service.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_Service"
contract="SilverlightApplication3.ServiceReference1.Service"
name="BasicHttpBinding_Service." />
</client>
</system.serviceModel>
</configuration>
The problem here is the endpoint address that points to localhost port 4370. So as this service is part of the same website using a relative Uri sounds like the reasonable thing to do. Unfortunately this doesn't work and results in an UriFormatException with message: "Invalid URI: The format of the URI could not be determined.". Not good. So this means we have to change the address for every local endpoint when we move the Silverlight application, and its WCF services, to a different location. Not good, specially when we could have a long list of services used
.
A partial solution
So fortunately there is a way around this as we can specify the service address when creating the WCF service proxy, all we need to do is insert the correct Uri based upon the location of the Silverlight application.
Normally the code I write looks something like this:
ServiceClient proxy = new ServiceClient();
proxy.DoWorkCompleted += proxy_DoWorkCompleted;
proxy.DoWorkAsync();
What I need to do is change this somewhat so that the ServiceClient is created through a factory method like this:
ServiceClient proxy = GetServiceClient();
proxy.DoWorkCompleted += proxy_DoWorkCompleted;
proxy.DoWorkAsync();
The interesting work occurs in the GetServiceClient() function:
private ServiceClient GetServiceClient()
{
Uri uri = new Uri(HtmlPage.Document.DocumentUri, "Service.svc");
EndpointAddress address = new EndpointAddress(uri);
return new ServiceClient("*", address);
}
First I determine where the Silverlight application was loaded from in the first place through the HtmlPage.Document.DocumentUri. Next I create a new Uri with the relative address where the service should be located. And finally I create and return the WCF proxy object. The first parameter, the "*" indicated that the WCF runtime should use a wildcard match and the first endpoint found with the same contract, which is the same as when no parameters are passed in.
A better solution
Of course the solution above works but requires extra coding and care with every service we create. And being easy to forget, specially when you just get started with Silverlight, is not exactly an ideal solution. So the proper solution is that the WCF stack inside of Silverlight should accept relative Uri and understand that this Uri is relative to the current page. Now this is not exactly rocket science and exactly what browsers have been doing for years. So lets hope the Silverlight team adds this feature before the RTM (which I hope is real soon).
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu