Silverlight and WCF Deployment Gotcha

Posted by: Malcolm Sheridan , on 7/20/2009, in Category Silverlight 2, 3, 4 and 5
Views: 31710
Abstract: The following article demonstrates how to overcome deployment issues when using Silverlight and WCF websites.
Silverlight and WCF Deployment Gotcha
 
Silverlight has been around for over a year now and it’s a great technology. One gotcha that I ran into after deploying my web application was any calls from the Silverlight application to a local WCF service stopped working. I tested it locally before deploying and everything worked. I quickly realised that the endpoint address in the ServiceReferences.ClientConfig configuration file was pointing to my development server, not the production server. This file is compiled into the .xap file that is deployed to your web server, so instead of changing the endpoint address before you deploy, I’ll show you a simple trick that allows your application to work in development, test and production environments with a two lines of code. This is achieved by using the System.Uri class.   By using this class you can tell your Silverlight application to point to a specific endpoint address instead of what’s defined in the ServiceReferences.ClientConfig file.
To begin with open Visual Studio 2008 and choose File > New > Project > Silverlight > Silverlight Application. Once that is done we need to create a Silverlight enabled WCF service. Right click the project and choose Add > New Item > Silverlight > Silverlight-enabled WCF service:
SilverlightApplication1
Name this service Person.svc. Once the file has been added to the project, add the following code:
C#
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Person
{
[OperationContract]
      public string GetSurname()
      {
            return "Sheridan";
}
}
 
VB.NET
<ServiceContract(Namespace := ""), AspNetCompatibilityRequirements(RequirementsMode := AspNetCompatibilityRequirementsMode.Allowed)> _
Public Class Person
<OperationContract> _
Public Function GetSurname() As String
             Return "Sheridan"
End Function
End Class
 
I have defined one public method called GetSurname which returns a string. I’m keeping this service as simple as possible for this example. Build your project before moving on.
Next open the Silverlight project. Add a reference to the WCF service we just created. Right click on the Silverlight project and choose Add Service Reference. The service reference dialog will appear. Click the Discover button and Visual Studio will automatically locate WCF services in the solution.
AddServiceReference
Now that we have the reference, we can turn our attention to the MainPage.xaml file. Add the following xaml:
<Grid x:Name="LayoutRoot">
    <StackPanel Orientation="Horizontal">
            <Button x:Name="btnGetSurname" Width="100" Height="25" Content="Go" Click="btnGetSurname_Click" />
            <TextBox x:Name="txtSurname" Width="100" Height="25" />
    </StackPanel>
 </Grid>
Now it’s time to add code to the Button click event to call the service. Normally you would code the application like the following to call the WCF service:
C#
PersonRef.PersonClient client = new PersonRef.PersonClient();
client.GetSurnameCompleted += delegate(object s, PersonRef.GetSurnameCompletedEventArgs args)
{
txtSurname.Text = args.Result;
};
client.GetSurnameAsync();
 
VB.NET
Dim client As New PersonRef.PersonClient()
AddHandler client.GetSurnameCompleted, Function(s, args) AnonymousMethod1(s, args)
client.GetSurnameAsync()
 
Private Function AnonymousMethod1(ByVal s As Object, ByVal args As PersonRef.GetSurnameCompletedEventArgs) As Boolean
txtSurname.Text = args.Result
      Return True
End Function
 
In the code above the code will call the WCF service and return the value. For this code to run, the application uses the ServiceReferences.ClientConfig configuration file and grabs the endpoint address value for the WCF service. On my machine the endpoint address is the following:
<client>
<endpoint address=http://localhost:4192/Person.svc binding="customBinding"
binding Configuration="CustomBinding_Person" contract="PersonRef.Person"
      name="CustomBinding_Person" />
</client>
As you can see from code above, the application will connect to http://localhost:4192/Person.svc. If you run the project locally it works. But what happens when you deploy this to a separate web server? Your code will fail because the endpoint address is pointing to your local machine. One workaround that I found good is to use the System.Uri class in your code to point to a WCF service that is relative to the website. To make this work replace the code previously added with the following:
C#
Uri uri = new Uri(Application.Current.Host.Source, "../Person.svc");
PersonRef.PersonClient client = new PersonRef.PersonClient("CustomBinding_Person", uri.AbsoluteUri);           
client.GetSurnameCompleted += delegate(object s, PersonRef.GetSurnameCompletedEventArgs args)
{
txtSurname.Text = args.Result;
};
client.GetSurnameAsync();
 
VB.NET
Dim uri As New Uri(Application.Current.Host.Source, "../Person.svc")
Dim client As New PersonRef.PersonClient("CustomBinding_Person", uri.AbsoluteUri)
AddHandler client.GetSurnameCompleted, Function(s, args) AnonymousMethod1(s, args)
client.GetSurnameAsync()
 
Private Function AnonymousMethod1(ByVal s As Object, ByVal args As PersonRef.GetSurnameCompletedEventArgs) As Boolean
txtSurname.Text = args.Result
      Return True
End Function
In the code above I have created an Uri object which points to the Person WCF service in the web application. Since the code is being executed from the xap file, the URI is relative to the website. On the next line, the endpoint configuration name is used, CustomBinding_Person, and the remote address value is the uri.AbsoluteUri value
C#

Uri uri = new Uri(Application.Current.Host.Source, "../Person.svc");
PersonRef.PersonClient client = new PersonRef.PersonClient("CustomBinding_Person", uri.AbsoluteUri);  
 
VB.NET

Dim uri As New Uri(Application.Current.Host.Source, "../Person.svc")
Dim client As New PersonRef.PersonClient("CustomBinding_Person", uri.AbsoluteUri)
 

If you build this application, it can now be run locally or on a dedicated web server and the code will work without having to update the configuration file. Perfect!

The entire source code of this article can be downloaded over here

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce the The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this eBook aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on .NET Standard and the upcoming C# 8.0 too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Malcolm Sheridan is a Microsoft awarded MVP in ASP.NET, a Telerik Insider and a regular presenter at conferences and user groups throughout Australia and New Zealand. Being an ASP.NET guy, his focus is on web technologies and has been for the past 10 years. He loves working with ASP.NET MVC these days and also loves getting his hands dirty with jQuery and JavaScript. He also writes technical articles on ASP.NET for SitePoint and other various websites. Follow him on twitter @malcolmsheridan


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
Comment posted by Gopi on Monday, July 20, 2009 1:01 PM
Hi,
Thanx for the article.
i have one question.
Where are your hosting the created wcf service? in IIS or some where else?
I have created a WCF service and hosted it in IIS in a system 'A' and now i am accessing this service from another machine 'B' by adding service ref  as http://A/serive.svc. Well as of now it didn't it dint give me any trouble but is this the safe to do like this?
Comment posted by Malcolm Sheridan on Monday, July 20, 2009 8:58 PM
@Gopi
The service was being hosted in IIS.  This article explains about referencing a WCF service in the same solution, so when you deploy, you're not referencing the WCF service via the config file.
Comment posted by Soctt Jason on Wednesday, July 22, 2009 5:37 PM
thank you. this is excatly what I was looking for.  Solve my majoy problem.  but i have a small question, why "../" in "../Person.svc"?  thank you again.
Comment posted by Malcolm Sheridan on Wednesday, July 22, 2009 7:28 PM
@Scott
The reason for that is because the code is run from the xap file, which is located in the ClientBin folder of the website.  
Comment posted by Nitin on Thursday, September 24, 2009 6:14 PM
thank you so much. it solved my issue
Comment posted by Greg on Tuesday, November 17, 2009 7:25 PM
I have implemented for my application because I was having the same issue and now, when I try to run it on my development machine, I get the following error:

System.ArgumentException was unhandled by user code
  Message="The provided URI scheme 'file' is invalid; expected 'http'.\r\nParameter name: via"
  StackTrace:
       at System.ServiceModel.Channels.TransportChannelFactory`1.ValidateScheme(Uri via)...

This gets thrown when I attempt to instiate my WCF service client, with the following signature:
    Uri uri = new Uri(Application.Current.Host.Source, "../WebServices/MyWCFService.svc");
                    _myServiceClient = new MyServiceClient("BasicHttpBinding_MyWCFService", uri.AbsoluteUri);
                
If I examine the client parameter being passed to the ClientServiceChannel constructor I see that its endpoint is "file:///C:/Users/Me/Documents/MyProjects/MySolution/MyApp/Bin/WebServices/MyWCFService.svc" (folder and filenames changed for privacy).

I'm obviously missing something. Any ideas?
Comment posted by Sumit on Monday, August 16, 2010 3:32 AM
Nice 'hack' if I may dare to say so. But thanks for putting it up. I've been looking high and low for such a solution.

Root of the problem, changing configuration information before deploying to different environment is really self-defeating the whole 'configuration file' concept. In this case, I guess the argument is - 'because silverlight's xap file is a executed on client side, it must know the service location before download and hence the webservice binding must happen at build time!!!!' but still if I have a configuration I expect to update it and 'things' dependent on the configuration (like webservice references) should pick them up on change!!!

Anyway, enough of ranting, thanks for the solution again!!!
Comment posted by Skitz on Saturday, November 6, 2010 5:40 AM
Thank you!!!
Comment posted by motmeister on Sunday, February 27, 2011 10:12 PM
I grasped the concepts of the web service easily, and there are MANY examples of how to get a web service working with Silverlight on localhost (outside of IIS). I had a great example that was working fine while in the VS2010 environment. Easy-peasy, let's just march on and deploy it in my shared web hosting environment.... NOT. I spent the better part of three days trying to get it to work. There are lots of postings out there with loose ends where people were trying to do the same thing. In long strings of comments, someone would say that after trying everything, they could still not get it to work and would someone explain why.... and no one ever DID. Some people claimed success and never fully explained how they got it to work, but many kept talking about failure, even after clientaccesspolicy.xml and crossdomain.xml were set up multiple different ways. The error I was getting, as were many others, said a lot about cross-domain, and SOAP blah-blah... (the message isn't important, because no one ever gave me a real FIX!). No matter where I put those darned xml files, the error message would NOT go away. As soon as I saw your post, I KNEW that the relative reference to the .svc file meant that it would DEFINITELY be in the same domain (NOT cross-domain), and it gave me hope after a lot of frustration. Sure enough, a few simple changes to my app to use the relative uri, and a republish to my shared server and ZOWIE! Now I finally have a concept piece to begin building on, with like, calling databases from the service, and all of the tricks needed to get my business app built and ready to start raking in the cash (*I WISH!***). Mr. Sheridan, Thank you SO much! You made my weekend!
Comment posted by janakiramangara on Wednesday, July 18, 2012 10:04 AM
Hi Malcom,
Nice article and very easy way to understand, i tried ur application i got result, but when i try to implement with  my requrement i am not getting any result,if i use the code without uri, working fine , but with uri i am getting nothing.
my service method is:
public string ConvertToJpg(byte[] raw, string fileName)
        {
            using (Image img = Image.FromStream(new MemoryStream(raw)))
            {
                try
                {
                    StringBuilder dateStamp = new StringBuilder();
                    dateStamp.Append(DateTime.Now.ToString());
                    dateStamp.Replace("/", "_").Replace(" ", "_").Replace(":", "_").ToString();
                    dateStamp.Append(".jpg");

                    string path = "D:\\"+fileName + dateStamp;
                    img.Save(path, ImageFormat.Jpeg);
                    return "File Saved Successfully";
                }
                catch (Exception)
                {
                    return "Unable to Save the File Please Check Path n FileName";
                }
                
            }
        }
And My Button_click Code is:
byte[] buffer = GetBuffer(wb);
            string fileName = txtsavePath.Text;

            Uri uri = new Uri(Application.Current.Host.Source, "../SaveFileService.svc");
            WcfRef.SaveFileServiceClient client = new WcfRef.SaveFileServiceClient("CustomBinding_SaveFileService", uri.AbsoluteUri);
            client.ConvertToJpgCompleted += delegate(object s, WcfRef.ConvertToJpgCompletedEventArgs args)
            {
                txtsavePath.Text = args.Result;
            };
            client.ConvertToJpgAsync(buffer, fileName);

Would u mind Suggesting any Changes. would be a great hep

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

C# .NET BOOK

C# Book for Building Concepts and Interviews

Tags

JQUERY COOKBOOK

jQuery CookBook