(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

WP7 Stock Quote Demo Part 4 : Push Server side

(0 votes)
John McFetridge
>
John McFetridge
Joined Feb 12, 2010
Articles:   5
Comments:   0
More Articles
0 comments   /   posted on Sep 27, 2010
Tags:   windows-phone-7 , push-notifications , john-mcfetridge
Categories:   Windows Phone

This article is compatible with the latest version of Silverlight for Windows Phone 7.

This article is Part 4 of the series WP7 Stock Quote Demo.

Now we have shown the development of the UX for the quoting application however the real value in this is the setting of alerts that specifies when the user wants to be notified of a change in the value of a stock. Microsoft Push Notification (MPN) is used to setup a push notification. Thus Silverlight is only one part of the development of a robust phone application as we must also be proficient in writing .Net services. Some argue that most phone applications will be Cloud application but I think that is little naive as there will always be some suited to phone only code such as a game. As this diagram below shows the phone client will setup the MPN service and send the alert data to our Quote service . At this point the phone application is still polling Yahoo for data but then as soon as the user closes the phone application by touching the Back button our phone application is terminated and the server version takes over. Overall the solution looks like:

The Notification channel is setup: Diagram 2

 

I will go through the implementation of  this solution that adds code to the client and build the Quote Server. Let’s start with the server side where the idea here is to develop a WCF service that will host the same Yahoo retrieval code that we placed into the Phone UX. This is pretty interesting when you consider that I was able to move this Silverlight C# code almost without change to my server. This is the real power of the Microsoft solution as we have the CLR everywhere and it lets the same skills be used on client/phone and server! I love it. I base my service on the Microsoft sample found http://windowsteamblog.com/windows_phone/b/wpdev/archive/2010/05/04/understanding-how-microsoft-push-notification-works-part-2.aspx . This is WPF application that is being used to host my WCF service and this is great for a demo application like this I normally would not employ this method as I would build a IIS hosted Restful service but that is the subject of a later article.To start I setup my ServiceContract that looks like:

 public interface IQuotePushService
     {
         [OperationContract,WebGet]
         void  SetQuoteInfo(string info);
         
         [OperationContract,WebGet]
        void Register(string uri);
  
         [OperationContract, WebGet]
         void Unregister(string uri);
     }
   

The WebGet attribute is making this a restful WCF method  so that we are able to make calls from the phone that look like:

http://localhost:8000/QuotePushService/Register?uri=foo.com

there is a good article on Restful WCF at, but in summary we are mapping the uri to methods on the service e.g. in this case the Register method is being called with a Uri parameter.

The SetQuoteInfo will allow the phone app to sent configuration info to the server to tell it what alerts are assigned to a given stock. Thus the server app has all the information needed to test for thresholds being exceeded.

 public void SetQuoteInfo(string infoXML)
         {
          
             XDocument xmlDoc = XDocument.Parse(infoXML);
             List<Quote> quotesList = (from Quote1 in xmlDoc.Descendants("Quote")
                    select new Quote
                   {
                        Symbol = Quote1.Element("Symbol").Value,
                        CompareGT = Quote1.Element("GTorLT").Value,
                        TargetPrice = Convert.ToDouble(Quote1.Element("Alert").Value)
                          
                    }).ToList();
              
             OnQuoteAlertRceived(quotesList.First());
    
         }

Tis method will accept as input an XML string describing the stock symbol that we want to monitor, it’s target price and a comparison operator that I am only supporting < and > right now. I then use a little Linq to XML to hydrate a list of quotes.

Now keep in mind that this is WCF service is being hosted in a WPF application, something that I would not do for a real service but that is the subject of another tutorial. As such I fire an event to the UI thread consistent with the behavior of the demo application by calling the eventhandler, QuoteAlertReceived. This results in the QuotePush_QuoteAlertReceived handler being called that starts the background thread to retrieve the quote info almost exactly like we did in the client application. So I added the StreamStockInfo class to the WPF server project. I had to make a minor change to pass the UI thread dispatcher to the timer as follows:

t = new Timer(new TimerCallback(GetData), Dispatcher.CurrentDispatcher, 0, 3000);

I then added the same DataAccess project without change so now I have my the quoting engine on the server side. Of course I had to add MVVMLight to my WPF project for my inter-thread communications. I then created almost the same delegate to handle the received stock quotes to the constructor for MainWindow.cs that I used in the phone app:

 Messenger.Default.Register<List<QuoteDTO>>(this, true,
                           q =>
                           {
                               q.ForEach(dto =>
                               {
                                   quotesList.ToList().ForEach(model =>
                                   {
                                       if (dto.Symbol.Contains(model.Symbol.ToUpper()))
                                       {
                                           model.Time = dto.Time;
                                           model.Trade = dto.Trade;
                                           model.Volume = dto.Volume;
                                           model.Ask = dto.Ask;
                                           model.Bid = dto.Bid;
                                           model.DayHi = dto.DayHi;
                                           model.DayLow = dto.DayLow;
                                           model.YrHi = dto.YrHi;
                                           model.YrLow = dto.YrLow;
                                           List<Uri> SubscribersForAlert = new List<Uri>();
                                           //the quote may have multiple subsribers with different criteria
                                           model.alertInfo.ForEach(a =>
                                           {
                                               bSendNotification = false;
                                               //only send alert 5 times
                                               if (a.numberFired <= 5000)
                                               {
                                                     if (a.Comparer == "true")
                                                     {
                                                         if (dto.Trade > a.Threshold)
                                                             SubscribersForAlert.Add(new Uri(a.PhoneID, UriKind.Absolute));
                                                     }
                                                     else
                                                     {
                                                         if (dto.Trade < a.Threshold)
                                                             SubscribersForAlert.Add(new Uri(a.PhoneID, UriKind.Absolute));
                                                     }
                                                     a.numberFired++;
                                               }
                                           });
  
                                           if (SubscribersForAlert.Count > 0)
                                           {
                                              
                                               ThreadPool.QueueUserWorkItem((unused) => notifier.SendTileNotification(SubscribersForAlert, "PushNotificationsToken", "snow.png", 2, model.Symbol, OnMessageSent));
  
                                             }
                                           
                                         }
                                   });
  
                               });
  
                           }
             );

However a Quote might have multiple subscribers and this code will look for this condition and fire 10 notifications to the phone if the condition persists.The target phone must have sent a Register command to our server which I arrange to do at the startup of the phone app.The Register method that is event labeled (4) in diagram 2.and is passed the Uri to send the push to and we just record this in a list of subscribers which will be used later to sent Alerts to.

The SendTileNotification method is contained in a PushNotificationManager.cs that the MS sample contains. This will create a formatted XML string for the Tile message (it also contains the logic for Toast). This XML looks like :

 <?xml version=\"1.0\" encoding=\"utf-8\"?> 
       <wp:Notification xmlns:wp=\"WPNotification\"> 
                 <wp:Tile>
                     <wp:BackgroundImage>{0}</wp:BackgroundImage>
                     <wp:Count>{1}</wp:Count>
                     <wp:Title>{2}</wp:Title>
                 </wp:Tile>
             </wp:Notification>

There are 3 parameters that are filled in by the code:

1. Image for the tile background

2. A count of the alerts send

3. A description like the symbol name

The method then encodes the XML and call SendMessage that builds an Http Post payload to deliver to the MPN server. This code looks like:

 //Create and initialize the request object
                 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(channelUri);
                 request.Method = WebRequestMethods.Http.Post;
                 request.ContentType = "text/xml; charset=utf-8";
                 request.ContentLength = payload.Length;
              
                 if (notificationType == NotificationType.Toast)
                 {
                     request.Headers.Add(notificationClassHeader, "2");
                     request.Headers.Add(targetHeader, "toast");
                 }
                 else if (notificationType == NotificationType.Tile)
                 {
                     request.Headers.Add(notificationClassHeader, "1");
                     request.Headers.Add(targetHeader, "token");
                 }
                 request.Headers.Add(messageIdHeader, Guid.NewGuid().ToString());
                 request.BeginGetRequestStream((ar) =>
                 {
                     //Once async call returns get the Stream object
                     Stream requestStream = request.EndGetRequestStream(ar);
   
                     //and start to write the payload to the stream asynchronously
                    requestStream.BeginWrite(payload, 0, payload.Length, (iar) =>
                     {
                         //When the writing is done, close the stream
                         requestStream.EndWrite(iar);
                         requestStream.Close();
  
                         //and switch to receiving the response from MPNS
                         request.BeginGetResponse((iarr) =>
                         {
                             using (WebResponse response = request.EndGetResponse(iarr))
                             {
                                 //Notify the caller with the MPNS results
                                OnNotified(notificationType, (HttpWebResponse)response, callback);
                             }
                         },
                         null);
                     },
                     null);
                 },
                 null);
             }
  

It sets 2 headers:

1. X-NotificationClass which is set to 1

2. X_WindowsPhone-Target which is set to toast

It then writes the XML into the requestStream and sets up a delegate to handle the response. This delegate will just display the message on the UI thread. As a result the alert is posted to the MPN server which is step (5) in diagram 2 then forwarded to the phone. We will discuss the Phone side in Part 5.

John McFetridge

jmcfet@bellsouth.net

Architect, Developer and Trainer for Sandkey Software which specializes in Silverlight and WP7 development 


Subscribe

Comments

No comments

Add Comment

Login to comment:
  *      *       

From this series