Over a million developers have joined DZone.

Push Notifications for Windows Phone 7 using SQL Azure and Cloud services - Part 1/3

· Mobile Zone

Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud, brought to you in partnership with IBM.

In this article series I will write about push notifications for Windows Phone 7 with focus on the server-side covering notification subscription management, subscription matching and notification scheduling. The server-side will be using SQL Azure and WCF Web Service deployed to Azure.

As an example I will create a very simple news reader application where the user can subscribe to receive notifications when a new article within a given category is published. Push notifications will be delivered as Toast notifications to the client. The server-side will consist of a SQL database and WCF Web Services that I deploy to Windows Azure.

Creating the SQL Azure database

The database will store subscription information and news articles. To achieve this I will create three tables; Category, News and Subscription.

The category table is just a place holder for categories containing a unique category ID and the category name. The news table is where we store our news articles containing a unique news ID, header, article, date added and a reference to a category ID. In the subscription table we store information about the subscribers containing a global unique device id, channel URI and a reference to a category ID.

I create the database in Windows Azure as a SQL Azure database. To learn how to sign up to SQL Azure and create a database there you can read an article I wrote earlier explaining how sign up and use SQL Azure. When you have signed up to SQL Azure and created the database you can use the following script to create the three tables we will be using in this article.

USE [PushNotification]
GO

CREATE TABLE [dbo].[Category]
(
[CategoryId] [int] IDENTITY (1,1) NOT NULL,
[Name] [nvarchar](60) NOT NULL,
CONSTRAINT [PK_Category] PRIMARY KEY ([CategoryId])
)
GO

CREATE TABLE [dbo].[Subscription]
(
[SubscriptionId] [int] IDENTITY (1,1) NOT NULL,
[DeviceId] [uniqueidentifier] NOT NULL,
[ChannelURI] [nvarchar](250) NOT NULL,
PRIMARY KEY ([SubscriptionId]),
[CategoryId] [int] NOT NULL FOREIGN KEY REFERENCES [dbo].[Category]([CategoryId])
)
GO

CREATE TABLE [dbo].[News]
(
[NewsId] [int] IDENTITY (1,1) NOT NULL,
[Header] [nvarchar](60) NOT NULL,
[Article] [nvarchar](250) NOT NULL,
[AddedDate] [datetime] NOT NULL,
PRIMARY KEY ([NewsId]),
[CategoryId] [int] NOT NULL FOREIGN KEY REFERENCES [dbo].[Category]([CategoryId])
)
GO

 
By running this script the three tables Category, Subscription and News that will be used in this example are created. Be aware that I have not normalized the tables to avoid complicating the code. You can create a normal MS SQL database and run it locally or at a server if you want to, but for this example I have used SQL Azure.

Creating the WCF Cloud service

The WCF Cloud service will be consumed by the WP7 app and will communicate with the SQL Azure database. The service is responsible for pushing notifications to the WP7 clients.

Steps to create the WCF Cloud service with Object Model

Create a Windows Cloud project in Visual Studio 2010, I named mine NewsReaderCloudService. Select WCF Service Web Role, I named mine NewsReaderWCFServiceWebRole. Then you need to create an Object Model that connects to your SQL Azure database, I named mine NewsReaderModel and I named the entity NewsReaderEntities. For a more detailed explanation on how to do this you can take a look at an article I have written earlier on the subject.

If you have followed the steps you should now have a Windows Cloud project connected to your SQL Azure database.

Creating the WCF contract

The WCF contract is written in the auto generated IService.cs file. The first thing I do is to rename this file to INewsReaderService.cs. In this interface I have added seven OperationContract methods: SubscribeToNotifications, RemoveCategorySubscription, GetSubscribtion, AddNewsArticle, AddCategory, GetCategories and PushToastNotifications. I will explain these methods in the next section when we implement the service based on the INewsReaderService interface. Below you can see the code for INewsReaderService.cs

using System;
using System.Collections.Generic;
using System.ServiceModel;

namespace NewsReaderWCFServiceWebRole
{
[ServiceContract]
public interface INewsReaderService
{
[OperationContract]
void SubscribeToNotification(Guid deviceId, string channelURI, int categoryId);

[OperationContract]
void RemoveCategorySubscription(Guid deviceId, int categoryId);

[OperationContract]
List<int> GetSubscriptions(Guid deviceId);

[OperationContract]
void AddNewsArticle(string header, string article, int categoryId);

[OperationContract]
void AddCategory(string category);

[OperationContract]
List<Category> GetCategories();

[OperationContract]
void PushToastNotifications(string title, string message, int categoryId);
}
}

Creating the service

The service implements the INewsReaderService interface and this is done in the auto generated Service1.svc.cs file, the first thing I do is to rename it to NewsReaderService.svc.cs. I start with implementing members from the interface. Below you can see the code for NewsReaderService.svc.cs with empty implementations. In the next sections I will complete the empty methods implemented.

using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml;

namespace NewsReaderWCFServiceWebRole
{
public class NewsReaderService : INewsReaderService
{
public void SubscribeToNotification(Guid deviceId, string channelURI, int categoryId)
{
throw new NotImplementedException();
}

public void RemoveCategorySubscription(Guid deviceId, int categoryId)
{
throw new NotImplementedException();
}

public List<int> GetSubscriptions(Guid deviceId)
{
throw new NotImplementedException();
}

public void AddNewsArticle(string header, string article, int categoryId)
{
throw new NotImplementedException();
}

public void AddCategory(string category)
{
throw new NotImplementedException();
}

public List<Category> GetCategories()
{
throw new NotImplementedException();
}

public void PushToastNotifications(string title, string message, int categoryId)
{
throw new NotImplementedException();
}
}
}

SubscribeToNotification method

This is the method that the client calls to subscribe to notifications for a given category. The information is stored in the Subscription table and will be used when notifications are pushed.

public void SubscribeToNotification(Guid deviceId, string channelURI, int categoryId)
{
using (var context = new NewsReaderEntities())
{
context.AddToSubscription((new Subscription
{
DeviceId = deviceId,
ChannelURI = channelURI,
CategoryId = categoryId,
}));
context.SaveChanges();
}
}

RemoveCategorySubscription method

This is the method that the client call to remove a subscription for notifications for a given category. If the Subscription table has a match for the given device ID and category ID this entry will be deleted.

public void RemoveCategorySubscription(Guid deviceId, int categoryId)
{
using (var context = new NewsReaderEntities())
{
Subscription selectedSubscription = (from o in context.Subscription
where (o.DeviceId == deviceId && o.CategoryId == categoryId)
select o).First();
context.Subscription.DeleteObject(selectedSubscription);
context.SaveChanges();
}
}

GetSubscribtions method

 This method is used to return all categories which a device subscribes to.

public List<int> GetSubscriptions(Guid deviceId)
{
var categories = new List<int>();
using (var context = new NewsReaderEntities())
{
IQueryable<int> selectedSubscriptions = from o in context.Subscription
where o.DeviceId == deviceId
select o.CategoryId;
categories.AddRange(selectedSubscriptions.ToList());
}
return categories;
}

AddNewsArticle method

This method is a utility method so that we can add a new news article to the News table. I will use this later on to demonstrate push notification sent based on content matching. When a news article is published for a given category only the clients that have subscribed for that category will receive a notification.

public void AddNewsArticle(string header, string article, int categoryId)
{
using (var context = new NewsReaderEntities())
{
context.AddToNews((new News
{
Header = header,
Article = article,
CategoryId = categoryId,
AddedDate = DateTime.Now,
}));
context.SaveChanges();
}
}

AddCategory method

This method is also a utility method so that we can add new categories to the Category table.

public void AddCategory(string category)
{
using (var context = new NewsReaderEntities())
{
context.AddToCategory((new Category
{
Name = category,
}));
context.SaveChanges();
}
}

GetCategories method

This method is used by the client to get a list of all available categories. We use this list to let the user select which categories to receive notifications for.

public List<Category> GetCategories()
{
var categories = new List<Category>();
using (var context = new NewsReaderEntities())
{
IQueryable<Category> selectedCategories = from o in context.Category
select o;
foreach (Category selectedCategory in selectedCategories)
{
categories.Add(new Category {CategoryId = selectedCategory.CategoryId, Name = selectedCategory.Name});
}
}
return categories;
}

PushToastNotification method

This method will construct the toast notification with a title and a message. The method will then get the channel URI for all devices that have subscribed to the given category. For each device in that list, the constructed toast notification will be sent.

public void PushToastNotifications(string title, string message, int categoryId)
{
string toastMessage = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>{0}</wp:Text1>" +
"<wp:Text2>{1}</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";

toastMessage = string.Format(toastMessage, title, message);

byte[] messageBytes = Encoding.UTF8.GetBytes(toastMessage);

//Send toast notification to all devices that subscribe to the given category
PushToastNotificationToSubscribers(messageBytes, categoryId);
}

private void PushToastNotificationToSubscribers(byte[] data, int categoryId)
{
Dictionary<Guid, Uri> categorySubscribers = GetSubscribersBasedOnCategory(categoryId);

foreach (Uri categorySubscriberUri in categorySubscribers.Values)
{
//Add headers to HTTP Post message.
var myRequest = (HttpWebRequest) WebRequest.Create(categorySubscriberUri); // Push Client's channelURI
myRequest.Method = WebRequestMethods.Http.Post;
myRequest.ContentType = "text/xml";
myRequest.ContentLength = data.Length;
myRequest.Headers.Add("X-MessageID", Guid.NewGuid().ToString()); // gives this message a unique ID

myRequest.Headers["X-WindowsPhone-Target"] = "toast";
// 2 = immediatly push toast
// 12 = wait 450 seconds before push toast
// 22 = wait 900 seconds before push toast
myRequest.Headers.Add("X-NotificationClass", "2");

//Merge headers with payload.
using (Stream requestStream = myRequest.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
}

//Send notification to this phone!
try
{
var response = (HttpWebResponse)myRequest.GetResponse();
}
catch(WebException ex)
{
//Log or handle exception
}

}
}

private Dictionary<Guid, Uri> GetSubscribersBasedOnCategory(int categoryId)
{
var categorySubscribers = new Dictionary<Guid, Uri>();

using (var context = new NewsReaderEntities())
{
IQueryable<Subscription> selectedSubscribers = from o in context.Subscription
where o.CategoryId == categoryId
select o;
foreach (Subscription selectedSubscriber in selectedSubscribers)
{
categorySubscribers.Add(selectedSubscriber.DeviceId, new Uri(selectedSubscriber.ChannelURI));
}
}
return categorySubscribers;
}

Running the WCF Cloud service

The WCF Cloud service is now completed and you can run it locally or deploy it to Windows Azure.

Deploying the WCF service to Windows Azure

Create a service package

Go to your service project in Visual Studio 2010. Right click the project in the solution explorer and click “Publish”, select “Create service package only” and click “OK”. A file explorer window will pop up with the service package files that got created. Keep this window open, you need to browse to these files later.

Create a new hosted service

Go to https://windows.azure.com and login with the account you used when signing up for the SQL Azure. Click “New Hosted Service” and a pop up window will be displayed. Select the subscription you created for the SQL Azure database, enter a name for the service and a url prefix. Select a region, deploy to Stage and give it a description. Now you need to browse to the two files that you created when you published the service in the step above and click “OK”.  Your service will be validated and created (Note that this step might take a while). In the Management Portal for Windows Azure you will see the service you deployed in Hosted Services once it is validated and created.

Test the deployed WCF service with WCF Test Client

Open the WCF Test Client, on my computer it's located at: C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\WcfTestClient.exe

Go to the Management Portal for Windows Azure and look at your hosted service. Select the NewsReaderService (type Deployment) and click the DNS name link on the right side. A browser window will open and most likely display a 403 Forbidden error message. That's OK, we are only going to copy the URL. Then select File and Add Service in WCF Test Client. Paste in the address you copied from the browser and add /NewsReaderService.svc to the end, click OK. When the service is added you can see the methods on the left side, double click AddCategory to add some categories to your database. In the request window enter a category name as value and click the Invoke button.

You can now log in to your SQL Azure database using Microsoft SQL Server Management Studio and run a select on the Category table. You should now see the Categories you added from the WCF Test Client.

Part 2

In part 2 of this article series I will create a Windows Phone 7 application that consumes the WCF service you just created. The application will also receive toast notifications for subscribed categories. Continue to part 2.

You can read my blog at www.breathingtech.com and follow me on twitter @PerOla

The Mobile Zone is brought to you in partnership with Strongloop and IBM.  Visually compose APIs with easy-to-use tooling. Learn how IBM API Connect provides near-universal access to data and services both on-premises and in the cloud.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}