views:

950

answers:

2

Hi, I'm trying to get a Server application to expose some status information using WCF. In particular I'm after using WCF services with RESTful "API". I'm hitting somewhat of a wall when it comes to consuming the REST api from a silverlight app/page that I want to have as an additional type of client...

So far I've been successful in defining a status interface:

public static class StatusUriTemplates
{
  public const string Status = "/current-status";
  public const string StatusJson = "/current-status/json";
  public const string StatusXml = "/current-status/xml";
}
[ServiceContract]
public interface IStatusService
{
  [OperationContract]
  [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = StatusUriTemplates.StatusJson)]
  StatusResultSet GetProgressAsJson();

  [OperationContract]
  [WebGet(ResponseFormat = WebMessageFormat.Xml, UriTemplate = StatusUriTemplates.StatusXml)]
  StatusResultSet GetProgressAsXml();

  [OperationContract]
  [WebGet(UriTemplate = StatusUriTemplates.Status)]
  StatusResultSet GetProgress();
}

Implementing it in the server:

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  public class ServerStatusService : IStatusService
  {
    public StatusResultSet GetProgressAsJson()
    { return GetProgress(); }

    public StatusResultSet GetProgressAsXml()
    { return GetProgress(); }

    public StatusResultSet GetProgress()
    {
       return StatusResultSet.Empty;
    }
  }

Exposing it from my code at runtime:

  var service = new ServerStatusService();
  var binding = new WebHttpBinding();
  var behavior = new WebHttpBehavior();

  var host = new WebServiceHost(service, new Uri("http://localhost:8000/server"));
  host.AddServiceEndpoint(typeof(IStatusService), binding, "status");
  host.Open();

I've even been successful with consuming the service from a .NET console/winfoems/WPF application using something along the line of this:

  var cf = new WebChannelFactory<IStatusService>(new Uri("http://localhost:8000/server/status"));
  var ss = cf.CreateChannel();
  Console.WriteLine(ss.GetProgress().TimeStamp);

The "wall" I'm hitting is that there is NO WebChannelFactory for SliverLight.

Period.

This means that when it comes to silverlight code, my options are:

  • Write ugly code using WebClient, which ultimately means I will have to update two sets of code whenever I have a change to my API
  • Use SOAP/WS for the WebService and keep updating the service reference from Visual Studio

Is there a way to keep the "clean" implementation with WebChannelFactory in SilverLight? Perhaps a public domain / open source WebChannelFactory for SilverLight?

Any help with this will be greatly appreciated!

+1  A: 

I almost hate to suggest it but would you feel comfortable with reimplementing the WebChannelFactory<T> class?

From a cursory glance through the Silverlight API it looks like you won't get much help from Microsoft out of the box. You'd need to reimplement a channel class and a factory for it.

Perhaps another way to create the channel and to isolate yourself from the platform-specific code is to create a custom implementation of it? Specifically what I mean is, you create yet another factory class, and the factory class either calls to the WebChannelFactory when it's available, or goes through the hoops of setting it up for you.

Sorry I don't have a more in-depth suggestion. :)

Rob
I think I will follow your proposition on re-implementing the factory class.I think I'll use something like LinFu to do all the heavy lifting.I just need to see if LinFu works on Silverlight
damageboy
+2  A: 

If this is a simple Xml REST service, why not use the WebClient in Silverlight to capture the XML using Linq to XML? I know you said its messy, but it all depends on how you look at it. if you change your service interface at anytime you're going to have to update your code in multiple places. Thats just the way it is.

So to do this, you will need to capture the data in an async fashion from the WebClient and then parse it with LINQ to XML.

Time Heuer has a good example on his site: http://timheuer.com/blog/archive/2008/03/14/calling-web-services-with-silverlight-2.aspx

Essentially, it looks like this:

WebClient rest = new WebClient();
rest.DownloadStringCompleted += new DownloadStringCompletedEventHandler(rest_DownloadStringCompleted);
rest.DownloadStringAsync(new Uri("http://example.org/current-status/xml"));

Then in your "rest_DownloadStringCompleted" you'd parse the string as XML. Like so:

string data = e.Result;
string url = string.Empty;

XDocument doc = XDocument.Parse(e.Result);
var myResults = from results in doc.Descendants("myXmlElement") ... blah blah blah

I've done the same thing with home grown REST Services from WCF and Silverlight and it worked great.

Donn Felker
I don't like this sort of solution because it created a dependency between the client and server that is not expressed in code.If I were to refactor / change the way my service worksI would basically have to make sure that changes were made in the server + client. The proxy eliminates that need.
damageboy