What's the most portable way to make a Silverl

2019-03-20 18:27发布

问题:

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:

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. :)



回答2:

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.



回答3:

So far I have found a few alternatives to WebChannelFactory for consuming REST services in Silverlight. They have all seen praise in forums and blogs, but I have yet to try any of them myself. I believe all three use generics to easily deserialize request responses into CLR objects.

  • RestSharp
  • REST Client for Silverlight, ported from WCF REST Starter Kit
  • Hammock

I am leaning towards RestSharp, because its examples look both simple and extensible to me.



回答4:

You are missing Spring.Rest : http://springframework.net/index.html#spring-rest-1.0.0-released



回答5:

I recently ran into the same problem and decided to create a class that has a simplified REST client interface for Silverlight, more or less like WebChannelFactory. Has synchronous-like behavior also.

http://regular-language.blogspot.com/2011/06/wcf-webhttp-rest-client-for-silverlight.html