Bing Search RSS with Silverlight 3 RIA Domain Service

Use the Silverlight 3 RIA Services DomainService class to consume RSS Search results from Bing.com

Bing.com, Microsoft's new search engine, provides RSS feeds of all Web searches. All one needs to do is append "&format=rss" to the search querystring. Additionally, you can add "&count=200" to get up to (but not more than) 200 search results. So a canonical  "template-ized" Bing.com RSS Search url would look something like this:

string url = "http://www.bing.com/search?format=rss&count=200&q=" + srch;

where "srch" is the user's search term (which can include any of the very useful  additional search operators, such as "site:" , "contains:", "hasfeed:", and so on.

You can also search feeds with a search url constructed like the following:

http://www.bing.com/search?q=feed%3A+Silverlight&format=rss&count=200

Unfortunately, bing.com does not expose either a clientaccesspolicy.xml or a crossdomain.xml policy file, so consuming the RSS search results directly from a Silverlight client application is not possible. We would either have to use a proxy, such as Yahoo Pipes, or - as in this case, I simply use a Silverlight 3 RIA Domain service that is deployed to the server alongside the actual Silverlight app. (NOTE: the Live.com / Bing.com web API does expose policy files).

You do not need to have a DataModel to use the new Silverlight 3 Domain Service class - it has the ability to add  [ServiceOperation] custom methods. As long as they return one of the listed types, the Silverlight 3 RIA Services infrastructure will automatically take care of autogenerating the client-side proxy class deriving from DomainContext, which you can happily program against in your Silverlight 3 Application. You'll be provided with all the asynchronous methods and events you need, right out of the box. No need to add ServiceReferences, either. You can even share classes between server and client by naming them as "MyClass.shared.cs". There is a special build rule that will automatically copy such files over to your Silverlight app. In this case, since XElement is one of the supported types for the ServiceOperation attributed  method type,  I am "good to go".

In this example I provide two ways to consume the feed results. First, I simply return the Xml data as a string, and we use XElement.Parse at the client, along with a simple LINQ query, to create a List< RssItem> that is bound to a DataGrid with a StackPanel DataTemplate for the layout. The other method takes advantage of the fact that XElement is one of the supported return types for the DomainService. In that case, I use XElement.Parse on the server, and return the XElement object to the Silverlight Client.

We have a simple page with a TextBox for the search phrase at the top, along with two buttons - one to use the string method, and the other to use the XElement method. Either button will return identical results:

Let's have a look at the server side DomainService class first:

using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Web.Ria;
    using System.Web.Ria.Data;
    using System.Web.DomainServices;

    [EnableClientAccess()]
    public class BingService : DomainService
    {
         [ServiceOperation]
         public string RSSResult(string srch)
        {
             string result = String.Empty;
            WebClient wc = new WebClient();
             string url = "http://www.bing.com/search?format=rss&count=200&q=" + srch;
             try
            {
                result = wc.DownloadString(url);
             }
             catch (WebException wex)
            {
                result = wex.Message;
             }
             finally
            {
                 wc.Dispose();
             }
             return result;
        }

         [ServiceOperation]
         public XElement RSSResultXml(string srch)
        {
             string result = String.Empty;
            WebClient wc = new WebClient();
             string url = "http://www.bing.com/search?format=rss&count=200&q=" + srch;
             try
            {
                result = wc.DownloadString(url);
             }
             catch (WebException wex)
            {
                result = wex.Message;
             }
             finally
            {
                wc.Dispose();
            }
            XElement xeresult = XElement.Parse(result);
             return xeresult;
        }
     }

Above, you see the two ServiceOperation methods. We just take the search term that is sent via the Silverlight Client, append it to the "Template" url for a search, and use the WebClient class to download the result string. Depending on which method used, we either load this into an XElement, or return the raw string. Note the required [EnableClientAccess()] attribute at class level.

When you build this, it will autogenerate your DomainContext class in the client Silverlight app - ready to use (in this case, "BingContext"); the following code shows the usage:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using BingSearch.Web;
using System.Xml.Linq;


namespace BingSearch
{
    public partial class MainPage : UserControl
    {
         public MainPage()
        {
             InitializeComponent();
        }


         private void Button1_Click(object sender, RoutedEventArgs e)
        {
            string srch = this.searchTerm.Text;
            BingContext ctx = new BingContext();
            ctx.RSSResultCompleted += new EventHandler<System.Windows.Ria.Data.InvokeEventArgs>(ctx_RSSResultCompleted);
            ctx.RSSResult(srch);
        }

         void ctx_RSSResultCompleted(object sender, System.Windows.Ria.Data.InvokeEventArgs e)
         {
             string rss = (string)e.ReturnValue;
            XElement elem = XElement.Parse(rss);
            var items = elem.Descendants("item");
            var i = items.FirstOrDefault();
             string s =i.Element("description").Value!=null? (string)i.Element("description").Value:String.Empty;
             string t = (string)i.Element("title").Value;
             string l = (string)i.Element("link").Value;
             string p = (string)i.Element("pubDate").Value;

            var results = (from itm in items
                           select new RssItem()
                           {
                               title = (string)itm.Element("title").Value,
                               description = (string)itm.Element("description").Value,
                               link = (string)itm.Element("link").Value,
                               pubDate = (string)itm.Element("pubDate").Value
                           }).ToList();
             this.rssGrid.ItemsSource = results;
         }


        private void Button2_Click(object sender, RoutedEventArgs e)
        {
            string srch = this.searchTerm.Text;
            BingContext ctx = new BingContext();
            ctx.RSSResultXmlCompleted += new EventHandler<System.Windows.Ria.Data.InvokeEventArgs>(ctx_RSSResultXmlCompleted);
            ctx.RSSResultXml(srch);
        }

         void ctx_RSSResultXmlCompleted(object sender, System.Windows.Ria.Data.InvokeEventArgs e)
        {
            XElement elem =(XElement) e.ReturnValue;
            var items = elem.Descendants("item");
            var i = items.FirstOrDefault();
             string s = (string)i.Element("description").Value;
             string t = (string)i.Element("title").Value;
             string l = (string)i.Element("link").Value;
             string p = (string)i.Element("pubDate").Value;

            var results = (from itm in items
                           select new RssItem()
                           {
                               title = (string)itm.Element("title").Value,
                               description = (string)itm.Element("description").Value,
                               link = (string)itm.Element("link").Value,
                               pubDate = (string)itm.Element("pubDate").Value
                           }).ToList();
             this.rssGrid.ItemsSource = results;
         }
    }

There are many more features to Silverlight 3 RIA Services - validation, two-way databinding, paging, the DomainDataSource control, and much more. I hope this short sample will give you some good ideas.

You can download the Visual Studio 2008 / Silverlight 3 solution here.

By Peter Bromberg   Popularity  (4119 Views)