Статьи

Push-уведомления для Windows Phone 7 с использованием служб SQL Azure и Cloud — часть 3/3

В  первой части этой серии статей я создал веб-службу WCF, которая управляет взаимодействием между приложением Windows Phone 7 и базой данных SQL Azure, содержащей данные служб push-уведомлений. Во второй части этой серии статей мы создали приложение WP7, которое использовало службу WCF, которую мы создали в первой части, и чтобы внутреннему сервису было известно, какие push-уведомления были интересны, а также отображались всплывающие уведомления на основе выбора пользователя в Приложение WP7. В этой части мы более подробно рассмотрим службу WCF и то, как обрабатываются управление подпиской, сопоставление и планирование.

Уведомление об управлении подпиской

Выполнив шаги, описанные выше, теперь у вас есть решение, которое позволяет пользователю управлять своими подписками на уведомления из приложения. Пользователь может видеть, на какие категории он подписывается в настоящее время, и он может подписаться на новые категории или удалить подписку на категории, на которые он уже подписан.

В этом примере я решил создать это решение с нуля, создав базу данных, создав службу (которая отвечает за отправку уведомлений) и использовав службу в приложении WP7. Если вы хотите создать более сложное решение для подписки на уведомления, вы можете воспользоваться службами уведомлений SQL Server или, если вы работаете с BizTalk, вы можете использовать свойства привязки адаптера для SQL Server . Также можно настроить службу WCF для получения уведомлений о запросах .

Сопоставление контента с подписками

Поскольку пользователь подписывается на категории, у нас есть ссылка для сопоставления его подписок с контентом. В этом примере контент — это новостные статьи, добавленные для данной категории. До сих пор нам приходилось вручную вызывать метод PushToastNotification, и мы хотим изменить это сейчас.

Когда добавляется новостная статья, мы хотим, чтобы служба автоматически отправляла всплывающее уведомление всем устройствам, которые подписываются на категорию, к которой относится статья. Теперь, когда у нас есть база кода, это можно сделать очень просто, добавив еще несколько строк кода в класс NewsReaderService.svc.cs в сервисном проекте WCF.

В методе AddNewsArticle мы добавляем две строки кода в конце метода и добавляем новый метод с именем GetCategoryNameById . Сначала мы получаем имя категории, чтобы мы могли отобразить его в сообщении тоста, а затем вызываем метод PushToastNotification . Вы можете увидеть обновленный метод AddNewsArticle и новый метод GetCategoryNameById ниже.

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();
}
//Get category name
string categoryName = GetCategoryName(categoryId);
//Push toast notification
PushToastNotifications("New article", "Added in category: " +categoryName, categoryId);
}

private string GetCategoryName(int categoryId)
{
using (var context = new NewsReaderEntities())
{
var categoryName = (from o in context.Category
where o.CategoryId == categoryId
select o.Name).First();
return categoryName;
}
}

Теперь вы можете повторно развернуть службу и запустить приложение. Используйте тестовый клиент WCF, чтобы добавить новостную статью, и вы сразу же получите уведомление о тосте на свой телефон, если вы подписываетесь на категорию, для которой вы добавили новостную статью.

Планирование уведомлений

В решении у нас теперь уведомления не запланированы, они отправляются сразу при добавлении новой статьи. Во многих ситуациях это нехорошо, вы не хотите спамить пользователей уведомлениями, и нагрузка на сервер будет довольно большой. В этом разделе мы рассмотрим, как мы можем планировать уведомления.

Для начала вы можете использовать встроенный пакетный механизм, где вы можете выбрать, нужно ли отправлять уведомления немедленно, каждые 450 секунд или каждые 900 секунд. Это можно увидеть в методе PushToastNotificationToSubscribeers . В этом примере я использую немедленное (значение 2), вы можете изменить это на 12 для партии 450 секунд или 22 для партии 900 секунд. Используя пакет 900 секунд, вы можете гарантировать, что пользователь будет получать уведомления только каждые 15 минут. Это немного помогает, но ваш сервис по-прежнему запрашивает базу данных и генерирует push-уведомления для каждой новой добавленной статьи.

Создание планировщика уведомлений

Сейчас я покажу, как вы можете создать поток, отвечающий за отправку уведомлений. Вы можете сделать это, создав поток и таймер, но я решил использовать проект с открытым исходным кодом, который называется «Легкое планирование задач» для .NET / Silverlight.

Использование облегченного планирования задач для .NET / Silverlight

Вы найдете проект в codeplex, и вам нужно скачать ZIP-файл, содержащий проект. Извлеките загруженный файл .zip и откройте проект в Visual Studio. Создайте проект, чтобы получить файл Hardcodet.Scheduling.dll . Убедитесь, что вы ориентируетесь на .NET Framework 4 при создании проекта, если только вы не можете столкнуться с некоторыми проблемами при использовании его в службе WCF. В вашем NewsReaderCloudService проекте добавить Hardcodet.Scheduling.dll файл в качестве ссылки. Теперь вы готовы начать использовать упрощенное планирование задач в своей службе WCF.

Расписание запускается каждый час

Теперь я хочу, чтобы мой сервис каждый час проверял новые статьи и отправлял уведомления подписчикам. Для этого добавьте следующий код в свой класс NewsReaderService.svc.cs

private readonly Scheduler _scheduler = new Scheduler();

public NewsReaderService()
{
CheckForNotificationEveryHour();
}

private void CheckForNotificationEveryHour()
{
//Configure the job to run every hour
Job consoleJob = new Job();
consoleJob.Run.Every.Hours(1);

//submit the job with the callback to be invoked
//Will invoke the NotifySubscribers method
_scheduler.SubmitJob(consoleJob, j => NotifySubscribers());
}

private void NotifySubscribers()
{
var categoryIds = GetIdForAllCategories();
var newArticlesDictionary = new Dictionary<int, int>();

//Set last notification check to one hour back since we are running this job every hour
var lastNotificationCheck = new DateTime(DateTime.Now.Millisecond - (60 * 60 * 1000));

//Check for new articles within each category
foreach (int categoryId in categoryIds)
{
using (var context = new NewsReaderEntities())
{
IQueryable<int> numberOfArticles = from o in context.News
where o.AddedDate >= lastNotificationCheck
select o.NewsId;
newArticlesDictionary.Add(categoryId,numberOfArticles.Count());
}
}
foreach (KeyValuePair<int, int> keyValuePair in newArticlesDictionary)
{
//Get category name
string categoryName = GetCategoryName(keyValuePair.Key);
//Push notifications to subscribers
PushToastNotifications("New articles", keyValuePair.Value + " new articles in " +categoryName, keyValuePair.Key);
}
}

private List<int> GetIdForAllCategories()
{
var categories = new List<int>();
using (var context = new NewsReaderEntities())
{
IQueryable<int> selectedSubscriptions = from o in context.Subscription
select o.CategoryId;
categories.AddRange(selectedSubscriptions.ToList());
}
return categories;
}

Имейте в виду, что в этом примере кода конструктор NewsReaderService не будет вызываться до вызова одной из служб.

Следующий шаг

Теперь у вас есть хорошая база кода для планирования уведомлений, и вы можете расширить ее с помощью необходимых вам функций. Одно расширение, которое может пригодиться, — это позволить пользователям выбирать расписание уведомлений при подписке на уведомления.

Для этого вы можете добавить в свою таблицу подписок новый столбец с именем Расписание. Вы расширяете метод SubscribeToNotification параметром TimeSpan .

[OperationContract]
void SubscribeToNotification(Guid deviceId, string channelURI, int categoryId, TimeSpan scheduling);

В NewsReaderService.svc.cs вы также расширяете метод SubscribeToNotification, чтобы расписание TimeSpan также добавлялось в базу данных. Затем вам нужно создать новое задание для этой подписки и добавить его в планировщик. Это будет выглядеть примерно так

private void ScheduleJob(Guid deviceId, int categoryId, TimeSpan schedule)
{
Job scheduledJob = new Job();
scheduledJob.Run.Every.TimeSpan(schedule);
_scheduler.SubmitJob(scheduledJob, job => NotifySubscriber(deviceId, categoryId, schedule));
}

private void NotifySubscriber(Guid deviceId, int categoryId, TimeSpan schedule)
{
//Check for new articles within the given category and
//push notification to the given device
}

Я надеюсь, что эти серии позволили вам лучше понять, как работать с push-уведомлениями для Windows Phone 7 и как вы можете управлять подписками на уведомления на стороне сервера. Дополнительные сведения о push-уведомлениях Microsoft см. Также в этом образце на AppHub:
Рецепт: вспомогательный вспомогательный компонент Notification Server для WP7

Вы можете прочитать мой блог на www.breathingtech.com и подписаться на меня в твиттере @PerOla