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 Smile. 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 Sad

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 Sad.

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

Published 26 August 2008 12:11 AM by Maurice

Comments

# Nikolaj Ravn said on 27 August, 2008 11:02 AM

Another issue to your fine solution is that what if you need to pass an argument from a Silverlight app to the service - as the proxy made for Silverlight only supports async calls.

I guess I just missed a very simple point here - but could you point out which?

Cheers, Nikolaj

# Maurice said on 28 August, 2008 12:22 PM

Hi Nikolaj,

Not sure I understand the question. just because calls are async doesn't mean you cannot pass parameters to a service call. And creating the proxy is local to the Silverlight client so no problems there either.

Maurice

# Donavan said on 20 October, 2008 02:17 PM

Is it possible to call a mysql db hosted on a unix/linux server using a wcf service from a silverlight site hosted on a windows server?

# Maurice said on 20 October, 2008 03:33 PM

If you have some sort of web service running on the Linux server you can call into that should be no problem. I don't know if MySql has something like data services out of the box though.

# Sam Gentile's Blog said on 02 December, 2008 01:38 PM

And that's it, summer is over. ASP.NET MVC ScottGu returns with ASP.NET MVC Preview 5 and Form Posting Scenarios Jeff Palermo has published another excerpt from his upcoming ASP.NET MVC in Action book, this time the Basics of MVC Routes LINQ K. Scott

Leave a Comment

(required) 
(required) 
(optional)
(required)