[Updated for Win8/Dev11 Beta]

In this post, we will do a walkthrough of creating a metro application calling a WCF service hosted locally on the same machine. Note that this is an application development/debug scenario only before you deploy your service on a different machine and this will not be supported for deployed metro applications.

First – a bit of an overview about the sample application. The C# metro application will be a photo viewing application with two user scenarios:

1) Users can view pictures from a source (here the source is going to be a WCF REST service and the metro application will use the new HttpClient to connect to the REST service)   
image     

2) Users can then ‘click’ or ‘tap‘ (if you are using the touch interface) a picture to get the view count on it (this View count will come from a WCF SOAP service and the metro application will use BasicHttpBinding to receive and send updates on the view count of the picture)

image

I am going to use a console application to host both my Picture REST service and ViewCount SOAP service.

Picture REST service

Code Snippet
  1. [ServiceContract]
  2. interface IPictureService
  3. {
  4. [WebGet]
  5. string[] GetPictureIds();
  6. [WebGet]
  7. Stream GetPicture(string PictureId);
  8. }

The service has two WebGet methods – one returning all the unique identifiers of the pictures available with the service and the other returning a stream representing the picture associated with a PictureId. [We are assuming that the service already has a collection of fantastic pictures and doesn’t need anyone uploading more to it Smile]

The implementation of the service is simple though GetPicture implementation might interest you a bit -

Code Snippet
  1. public Stream GetPicture(string PictureId)
  2. {
  3. FileStream file = new FileStream(PicturesCollection[PictureId].ToString(), FileMode.Open);
  4. WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
  5. return file;
  6. }
Here I am returning a picture image file whose path is determined by the PictureId and setting the content type of the response to ‘image/jpeg’. This is interesting because this will enable any client which knows how to process this content type to automatically do so without any other client programming required – e.g. if you call this REST service from the browser providing a valid picture id as recognized by this service, the service will return and the browser will automatically display the image file. You can also modify the ContentType to return data of any other format depending on your service.
Hosting the service is akin to what we always do:
Code Snippet
  1. // ***************   PICTURE REST SERVICE   ***************   
  2. string pictureServiceAddress = "http://localhost/PictureService";
  3. WebServiceHost pictureServiceHost = new WebServiceHost(typeof(PictureService), new Uri(pictureServiceAddress));
  4. // Add service endpoint & webHttp behavior
  5. pictureServiceHost.AddServiceEndpoint(typeof(IPictureService), new WebHttpBinding(), String.Empty)
  6.     .EndpointBehaviors.Add(new WebHttpBehavior() { HelpEnabled = true });
  7.  
  8. pictureServiceHost.Open();
        

Key pieces as you already might know for WCF REST services are – open up a “WebServiceHost”, add a “WebHttpBinding” and add a “WebHttpBehavior”.

ViewCount SOAP Service

Code Snippet
  1. [ServiceContract]
  2. interface IViewCount
  3. {
  4.     [OperationContract]
  5.     int GetViewCount(string PictureId);
  6.  
  7.     [OperationContract]
  8.     void UpdateViewCount(string PictureId);
  9. }

The service has two methods – one to get the view count of a picture and another to update it based on the Picture Id.

The only interesting thing about the service is that I am setting ServiceBehavior attribute with InstanceContextMode of single so that all concurrent accesses from multiple clients are processed sequentially to update the view count correctly.

Code Snippet
  1. // Setting instance context to single so that multiple clients can update sequentially
  2. [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  3. class ViewCountService : IViewCount

Again, service hosting is in the standard manner using BasicHttpBinding.

Code Snippet
  1. // ***************   VIEW COUNT SOAP SERVICE   ***************   
  2. string viewCountServiceAddress = "http://localhost/ViewCountService";
  3. ServiceHost viewCountServiceHost = new ServiceHost(typeof(ViewCountService), new Uri(viewCountServiceAddress));
  4. // Add service endpoints
  5. viewCountServiceHost.AddServiceEndpoint(typeof(IViewCount), new BasicHttpBinding(), String.Empty);
  6. // Enable metadata for the service
  7. ServiceMetadataBehavior metadatBehavior = new ServiceMetadataBehavior();
  8. metadatBehavior.HttpGetEnabled = true;
  9. viewCountServiceHost.Description.Behaviors.Add(metadatBehavior);
  10.  
  11. viewCountServiceHost.Open();

Metro application

This is a C# Windows Metro style application.

When the application starts up I want to display the images available with the service. So I use an HttpClient to make a request to my REST service and retrieve the Picture Ids and then I use my REST service http url to get the picture associated with each id and assign them to an Image control:

Code Snippet
  1. public async void DisplayImages()
  2. {
  3.     HttpClient client = new HttpClient();
  4.     
  5.     // Make an Http request to get the Picture Ids from the REST service
  6.     HttpResponseMessage pictureIdsResponse = await client.GetAsync(pictureServiceAddress + "/GetPictureIds");
  7.     if (pictureIdsResponse.IsSuccessStatusCode)
  8.     {
  9.         XDocument document = XDocument.Parse(pictureIdsResponse.Content.ReadAsStringAsync().Result);
  10.         // Parse the picture Ids
  11.         List<string> PictureIds = (from id in document.Root.Elements()
  12.                                    select id.Value).ToList();
  13.  
  14.         // Assign each image control a picture from the service using an HTTP url.
  15.         image1.Source = new BitmapImage(new Uri(String.Format("{0}/{1}={2}", pictureServiceAddress, "GetPicture?PictureId", PictureIds[0])));
         

Line 6 is where I am making a GET request to get the Picture Ids. The response is by default in Xml format so in Line 9, I am placing the response in an XDocument which I am then querying in line 11 to get the Picture Ids.

You can also make a call to the REST service from the browser directly with a Url  http://localhost/PictureService/GetPictureIds, to get back the Xml response:

image 

or the picture itself -

image

Now to view or update the view count – we need to access our WCF SOAP service. You need to generate a service reference to http://localhost/ViewCountService using the Add Service Reference (ASR) functionality. I mentioned the key differences in ASR generated code for Metro application in my previous post - http://blogs.msdn.com/b/piyushjo/archive/2011/09/22/wcf-in-win8-metro-styled-apps-absolutely-supported.aspx 

Once the pictures are displayed, I want to display its view count when it is clicked or tapped. So I am using the Tapped event to do so:

Code Snippet
  1. private void image_Tapped(object sender, Windows.UI.Xaml.Input.TappedEventArgs e)
  2. {
  3.     ShowImageDetails(((Windows.UI.Xaml.Controls.Image)sender).Tag.ToString());
  4. }
  5.  
  6. async void ShowImageDetails(string PictureId)
  7. {
  8.     // Set the description image
  9.     imageDetails.Source = new BitmapImage(new Uri(String.Format("{0}/{1}={2}", pictureServiceAddress, "GetPicture?PictureId", PictureId)));
  10.     
  11.     // Call the SOAP web service to get details - e.g. view count
  12.     PhotoService.ViewCountClient proxy = new PhotoService.ViewCountClient();
  13.     
  14.     // Update the view count with the current view
  15.     await proxy.UpdateViewCountAsync(PictureId);
  16.     
  17.     // And display the count
  18.     ViewCount.Text = String.Format("View Count = {0}", await proxy.GetViewCountAsync(PictureId));
  19.  
  20.     await proxy.CloseAsync();
  21. }

In Line 12, I am creating a proxy to my ViewCount SOAP service. In line 15, I am calling the UpdateViewCount method to go and update the view count for this Picture Id as someone tapped it to view it. And finally in line 18, I am getting the latest view count for this Picture Id by calling the GetViewCount method.

I have attached the service and the metro application solution. Both the REST and the SOAP services are hosted in the same console application and thus the same process. This service solution also contains a ConsoleClient to simulate a concurrently calling client which you can use to update the view count of pictures in the service which will then reflect in the Metro client UI when the metro client next calls the service.

The following is not needed anymore from BETA onwards and an exemption is automatically added while debugging from Visual Studio.

So now – you run the service and you run the metro client but you get -

System.Net.Http.HttpRequestException {"An error occurred while sending the request."} "Unable to connect to the remote server"

with the inner exception of {"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 127.0.0.1:80"}

Smile If it would have been straightforward – I wouldn’t be writing this blog post, now would I? By default, metro client access to loopback address is blocked for network isolation. See more here - http://msdn.microsoft.com/en-us/library/windows/apps/hh452759(v=vs.85).aspx.

So what do you do – you need to run the CheckNetworkIsolation.exe tool described in the above MSDN link like the following:

CheckNetIsolation.exe LoopbackExempt –a –n=”PackageFamilyName”

This will add an exemption for our metro application to access the loopback address.

You may ask – how do I get this PackageMonikerName. For this - you need to open up the app manifest file:

image

Not only this, I also had to open up the firewall for the TCP port my service is listening by creating an Inbound rule. This is required on the BUILD preview Win8 release.

Once you have done the above two steps – adding an exemption for your metro app and creating a firewall rule, the metro client should be able to connect to the locally hosted web services.

 

 

Thank you!