Silverlight Brass Tacks

Bill Reiss' Silverlight Ramblings
My upcoming Silverlight book for beginners Hello! Silverlight 2 with Dave Campbell, available online now!



Pages

Recent posts

Navigation

Archive

Blogroll

Tampa Divorce Lawyer

North of Tampa in Lutz, Florida. A Tampa Divorce Lawyer focusing on family, divorce, and real estate law.

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

Calling WCF on the server of origin from Silverlight

Let's say you're building a Silverlight app that uses WCF to talk to the same server that the web page is being served up from. You create your application, test it locally, it all works fine, but you go and deploy it to the server and it doesn't work any more. You look in Fiddler and don't see any traffic at all. If you debug, you may see an exception like this:

The remote server returned an unexpected response: (404) Not Found.
at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
at WCFToLocalServer.ServiceReference1.ServiceClient.ServiceClientChannel.EndDoWork(IAsyncResult result)
at WCFToLocalServer.ServiceReference1.ServiceClient.WCFToLocalServer.ServiceReference1.Service.EndDoWork(IAsyncResult result)
at WCFToLocalServer.ServiceReference1.ServiceClient.OnEndDoWork(IAsyncResult result)
at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)

Or worse, you get one of these:

System.ServiceModel.CommunicationException: An error occurred while trying to make a request to URI 'http://localhost:15840/WCFToLocalServerWeb/Service.svc'. This could be due to a cross domain configuration error. Please see the inner exception for more details. ---> System.Security.SecurityException ---> System.Security.SecurityException: Security error.
   at MS.Internal.InternalWebRequest.Send()
   at System.Net.BrowserHttpWebRequest.BeginGetResponseImplementation()
   at System.Net.BrowserHttpWebRequest.InternalBeginGetResponse(AsyncCallback callback, Object state)
   at System.Net.AsyncHelper.<>c__DisplayClass4.<BeginOnUI>b__3(Object sendState)
   --- End of inner exception stack trace ---
   at System.Net.AsyncHelper.BeginOnUI(BeginMethod beginMethod, AsyncCallback callback, Object state)
   at System.Net.BrowserHttpWebRequest.BeginGetResponse(AsyncCallback callback, Object state)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.CompleteSend(IAsyncResult result)
   at System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelAsyncRequest.OnSend(IAsyncResult result)
   --- End of inner exception stack trace ---
   at System.ServiceModel.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.ClientBase`1.ChannelBase`1.EndInvoke(String methodName, Object[] args, IAsyncResult result)
   at WCFToLocalServer.ServiceReference1.ServiceClient.ServiceClientChannel.EndDoWork(IAsyncResult result)
   at WCFToLocalServer.ServiceReference1.ServiceClient.WCFToLocalServer.ServiceReference1.Service.EndDoWork(IAsyncResult result)
   at WCFToLocalServer.ServiceReference1.ServiceClient.OnEndDoWork(IAsyncResult result)
   at System.ServiceModel.ClientBase`1.OnAsyncCallCompleted(IAsyncResult result)

What is going on?

The problem is that endpoints in the service client proxy code that gets generated use absolute URIs. This URI is stored in the ServiceReferences.ClientConfig:

<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_Service" maxBufferSize="65536"
                    maxReceivedMessageSize="65536">
                    <security mode="None" />
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:15840/WCFToLocalServerWeb/Service.svc"
                binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_Service"
                contract="WCFToLocalServer.ServiceReference1.Service" name="BasicHttpBinding_Service" />
        </client>
    </system.serviceModel>
</configuration>

This is fine if you're hitting a service like Digg or one of the other public web service APIs out there, but not if you want to be able to test on your own machine and also deploy the same XAP to your server without rebuilding.

You could go ahead and change this to the URI on your server and rebuild, but then it won't work locally for testing unless you have a crossdomain file on your server, and you would be hitting your server's web service, not your local one while testing.

So how do we get around this?

One workaround I've used is to dynamically build the service's URI based on the URI of the page so no matter where the app is running, it will call the right server. The generated proxy client code inherits from System.ServiceModel.ClientBase which has a few other constructors besides the default one which checks the config information to get the endpoint. The one we're interested in is this:

protected ClientBase(Binding binding, EndpointAddress remoteAddress); 

You'll have a line something like the following in your app:

ServiceReference1.ServiceClient svc = new WCFToLocalServer.ServiceReference1.ServiceClient();

Replace it with the following:

Uri uri = System.Windows.Browser.HtmlPage.Document.DocumentUri;
string host = uri.AbsoluteUri;
host = host.Substring(0, host.Length - uri.LocalPath.Length);
string servicePath = "/WCFToLocalServerWeb/Service.svc"
string serviceUri = host + servicePath;
ServiceReference1.ServiceClient svc = 
      new WCFToLocalServer.ServiceReference1.ServiceClient(new System.ServiceModel.BasicHttpBinding(),  new System.ServiceModel.EndpointAddress(serviceUri));

 

Make sure to change the servicePath in this sample to the actual location of your web service. You'll also have to change the object names to match your class names and namespaces generated by the "Add service reference" wizard.

Now no matter where you run the app, whether it's a dynamic port on the Visual Studio web server, or on your local IIS, or on your server, the web service located on the same host that the page is served up from will now get the request. You can check this out in Fiddler and confirm that it's going to the right place.

Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList
Posted: Aug 21 2008, 03:53 by Bill Reiss | Comments (12) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under:

Related posts

Comments

me3 dk said:

me3Bill

Well this is really a cool solution .
i see it as the same approach on how you retrive a absolute path to dabase
where you would not know the file path on the machine where the app would be installed.

Really a simple and clever solution.

Thanks so much for taking your time writting on this topic

# August 21 2008, 23:26

Ben Hayat us said:

Ben HayatBill;

Thank you very much. I was looking into this issue recently!

Thanks again!
..Ben

# August 22 2008, 06:33

Paul nl said:

PaulThanks! this solved my problem.

Paul

# September 25 2008, 04:48

senthil in said:

senthilStill the same problem exists after applying your code. I was created a WCF service solution, silver light application separately. I gave service reference to silver light application.

# September 28 2008, 23:43

Bill Reiss us said:

Bill ReissCan you run Nikhil's Web Development Helper http://www.nikhilk.net/ASPNETDevHelperTool.aspx and see where the request is going?

# September 29 2008, 00:55

TFisher us said:

TFisherWCF and LinqToSQL Question...
I created a Silverlight 2 Beta 2 app with WCF to call the products table from NWind. I have the clientaccesspolicy as well as the crossdomainpolicy files.

I can see the data on my local machine. App runs great. When I deploy the app to discountasp.net I don't see the data in the listbox.

The only difference I can find is the LinqToSql.dbml file points to my local nwind db. So I changed all the connectionstrings to point to the remote db.

Still no data...

Any ideas?

www.redpicket.com/.../SilverProductsTestPage.aspx

# October 02 2008, 07:30

Bill Reiss us said:

Bill ReissCan you see the traffic going back and forth in Nikhil's Web Development Helper listed above? You may be getting a server exception and that should show up in the reply message.

# October 02 2008, 08:17

Braulio es said:

BraulioWhat about having an http /cassini for development and an https for production?

Thanks
Braulio

# November 21 2008, 05:09

Sunny us said:

SunnyI have tried with your solution in my project. But, it is not working in my project. I have created a project and deployed it to through my local IIS and created a virtual directory for my silverlight web project. But, the grid is not showing data from the sql server. Please help me.
Thanx.
Sunny.

# December 16 2008, 08:47

sohbet said:

sohbetthank u
great post
love u all

# April 29 2009, 06:59

sohbet said:

sohbetthank u
great post
love u all

# April 29 2009, 06:59

Kurt Mang ca said:

Kurt MangThanks, that worked!

# May 07 2009, 10:53