Если вы следили за этой серией до сих пор , вы знаете, что мы продвинулись по пути из моего обзора серии , в котором описан целевой сценарий простой службы таблицы лидеров для пары игр на основе JavaScript для Windows 8, к статье из двух частей ( часть 1 / часть 2 ) о создании этой службы с использованием служб данных WCF и размещению в качестве облачной службы Azure , к самой последней статье , в которой я показал, как создать эту же службу с помощью ASP.NET Веб-API и размещен на веб-сайте Windows Azure .
Чтобы завершить группу решений, которые я изучаю в этой серии, в этой статье я покажу вам, как создать тот же сервис списка лидеров игр на основе новых мобильных служб Windows Azure (в предварительном просмотре на момент написания этой статьи).
Другой подход
В обеих предыдущих версиях службы списка лидеров решение включало серверное решение на основе кода. С другой стороны, в обоих случаях Visual Studio выполнял большую часть работы по созданию кода (вместе с Entity Framework, службами WCF Data Services и библиотеками Web API), но конечным продуктом является проект с исходным кодом, который должен поддерживаться
В отличие от этого, Windows Azure Mobile Services использует более простой подход, который позволяет мне, как разработчику, уделять больше внимания моему клиентскому коду, а не коду на стороне сервера.
Начиная
Как и в случае с другими моими примерами, вам понадобится учетная запись Windows Azure. Если у вас есть подписка MSDN , проверьте страницу со своими преимуществами, так как она может включать некоторые преимущества Windows Azure . Если у вас нет подписки MSDN, вы можете подписаться на 90-дневную пробную версию , которая предоставит вам все необходимое для выполнения, и по умолчанию включает ограничение расходов, чтобы гарантировать, что у вас нет — карманные расходы, если ваши услуги превысят лимиты пробного обслуживания (если ваши услуги превысят лимиты пробного периода, они будут автоматически закрыты).
Имея свою учетную запись Azure, я начинаю с входа на портал управления Windows Azure , который по умолчанию дает мне обзор всех служб, которые я запускаю в Windows Azure, включая все подписки, которыми управляет учетная запись, которую я использую войти, как вы можете видеть ниже:
Одна из «вкладок» с левой стороны — «МОБИЛЬНЫЕ УСЛУГИ», и это именно то, что я ищу, поэтому я нажму на нее, и я попаду на домашнюю страницу мобильных сервисов. Я удалил несколько существующих сервисов, с которыми работал, так что пока посмотреть особо нечего:
Я начну, щелкнув ссылку + NEW внизу (тоже будет работать ссылка в середине страницы), которая дает мне хороший вариант «Создать» ниже (и показывает, как мобильные сервисы вписываются в экосистему Azure). ):
Нажатие кнопки «Создать» дает мне следующий двухэтапный мастер для создания службы (поскольку я уже создал экземпляр базы данных в предыдущей части этой серии , я продолжу и использую этот существующий экземпляр базы данных):
Обратите внимание, что URL-адрес должен быть уникальным, и мастер динамически проверит имя и сообщит, доступно ли желаемое имя. В бесплатной версии мобильных служб все URL-адреса будут использовать шаблон MYSERVICENAME.azure-mobile.net, но я могу (за дополнительную плату) также настроить свою службу на использование настраиваемого доменного имени после завершения начальной настройки. Для моих целей подходит существующее доменное имя, поскольку конечные пользователи не будут взаимодействовать с сервисом напрямую.
Нажав на значок стрелки, вы попадете на вторую страницу мастера, где я предоставлю свои учетные данные базы данных:
Нажатие на значок галочки запустит процесс создания сервиса, который займет несколько минут. Когда служба настроена, я получаю экран, который выглядит следующим образом:
ПРИМЕЧАНИЕ. Имейте в виду, что хотя указанный выше URL-адрес службы является общедоступным, я могу или не могу сохранять его активным после завершения публикации, поэтому он может быть недоступен. Если вы хотите поэкспериментировать с мобильными сервисами, я призываю вас создать свой собственный сервис.
Что я сделал?
Хорошо, теперь у меня есть мобильный сервис. Но что это значит, точно?
По сути, за несколько коротких шагов я (с помощью Azure) создал инфраструктуру, которая предоставит мне:
- Место для хранения данных
- Возможность создавать таблицы и схемы (схемы являются динамическими по умолчанию… подробнее об этом позже)
- RESTful сервисы для доступа к данным в любых создаваемых мной таблицах
- Клиентские библиотеки для упрощения процесса взаимодействия с внутренними таблицами данных
- Услуги push-уведомлений
- Провайдеры аутентификации
Я расскажу о двух последних функциях в следующих статьях, но остальное, что мы получим, довольно круто. По сути, когда я создаю новую таблицу (вскоре я расскажу о том, как это сделать), Windows Azure Mobile Services автоматически создает службы RESTful, которые предоставляют базовые функции создания, чтения, обновления и удаления для этой таблицы. И в то время как мобильные сервисы также предоставляют клиентские библиотеки для нескольких клиентских платформ (и еще больше), базовые сервисы доступны через простые вызовы HTTP, поэтому они открыты для любой клиентской платформы, которая может выполнять вызовы HTTP.
А для людей, которым нравится прозрачность, клиентский SDK для мобильных сервисов разрабатывается на GitHub , так что вы можете проверить код, разветвить его, а также внести исправления ошибок и функции .
Быстрый старт
Теперь, когда мой мобильный сервис создан, я могу просто щелкнуть название сервиса, чтобы начать работать с ним. По умолчанию первый экран, который я вижу, это страница быстрого запуска, показанная ниже (отметьте флажок, который позволяет мне пропустить быстрый запуск в следующий раз):
Экран «Быстрый старт» предназначен для быстрого запуска пользователей с помощью своих мобильных сервисов и (на момент написания) поддерживает 3 клиентские платформы «из коробки»: Магазин Windows (приложения на C # или JavaScript), Windows Phone 8 и iOS. Поддержка Android также была объявлена.
Для каждой из этих клиентских платформ на странице «Быстрый старт» содержится руководство по созданию нового приложения для этой платформы с использованием новой мобильной службы или, альтернативно, для подключения существующего приложения к мобильной службе. Поскольку моя цель — подключить этот сервис к паре существующих игр, с этого я и начну.
Также обратите внимание, что на странице «Быстрый старт» есть ссылки на инструкции по добавлению аутентификации и push-уведомлений для каждой платформы (опять же, я покажу вам, как использовать эти функции в будущих публикациях).
Поскольку я собираюсь использовать свой сервис с приложением Магазина Windows, я переключусь на платформу Windows Store, затем щелкните заголовок «Подключить существующее приложение Магазина Windows», в котором приведены некоторые простые инструкции, как показано ниже:
Первые два шага довольно просты: один — установка Mobile Services SDK , а второй — ссылка на SDK в моем проекте приложения для Магазина Windows и добавление кода инициализации в проект. Код инициализации на шаге 2 предоставляет клиентской библиотеке URL-адрес для доступа к службе, а также ключ приложения (см. Выше), который является средством по умолчанию для аутентификации приложения в службе и авторизации доступа к таблице. Вкладка «Данные» для данной службы на портале управления позволяет устанавливать более строгие разрешения, но пока я буду придерживаться ключа приложения.
Я уже установил Mobile Services SDK, поэтому я покажу, как просто добавить ссылку на SDK. Я открою свое приложение для Магазина Windows (в данном случае это игра «Космический кадет», которую я использовал в качестве одного из моих демонстрационных приложений в этой серии), а в обозревателе решений щелкните правой кнопкой мыши узел «Ссылки» (который вы найдите прямо под узлом проекта) и выберите Добавить ссылку … Найдите JavaScript-клиент Windows Azure Mobile Services и установите флажок слева от него, как показано ниже:
Затем нажмите OK, чтобы добавить ссылку.
Шаг 3 в быстром старте показывает синтаксис, необходимый для добавления новых элементов в таблицу элементов, которую я могу создать, нажав кнопку «Создать элемент таблицы». В качестве альтернативы, если мне нужна таблица с собственным именем, я могу перейти на вкладку «Данные» и добавить туда новую таблицу:
Когда я нажимаю ссылку «Добавить таблицу» (или ссылку «Создать» в нижней части страницы), у меня появляется диалоговое окно, в котором можно указать имя таблицы, а также разрешения для вставки, обновления и Удалить и разрешения на чтение:
Как отмечалось ранее, по умолчанию любой пользователь, имеющий ключ приложения, может получить доступ к этим операциям на столе. Дополнительные разрешения включают «Все», которые открывают операцию любому, у кого есть URL-адрес службы (вы можете использовать это для предоставления открытого доступа к операции чтения, но это будет довольно редко использовать для других операций), Только для прошедших проверку пользователей и Только скрипты и админы. Последнее разрешение означает, что к таблице можно получить доступ только на портале управления и через серверные сценарии.
Я установлю имя таблицы на «gamescore», как показано выше, и нажму на галочку, чтобы создать таблицу. Когда создание таблицы завершено, если я щелкаю в таблице игровых очков и переключаюсь на вкладку Столбцы, я вижу, что определен только один столбец с именем id, и он проиндексирован, и имеет тип bigint.
Вы можете заметить, что здесь нет интерфейса для добавления столбцов. Это связано с тем, что по умолчанию мобильные службы используют модель динамической схемы, в которой все, что я добавляю в именованную таблицу из своего клиентского кода, автоматически генерирует необходимую схему на лету. Это обеспечивает очень гибкий механизм хранения данных, особенно если вы заранее не знаете, какие данные вам нужно хранить.
Подключение клиента
На данный момент у меня есть все, что мне нужно на стороне сервера, чтобы успешно подключить мою игру к сервису лидеров. Следующим шагом является подключение клиентского кода, и, как и в предыдущих примерах, я сделаю это, создав отдельный файл JavaScript, который содержит всю логику для взаимодействия со службой, и предоставлю это через определенный API с помощью WinJS. Функция .Namespace.define, как я это делал в предыдущих примерах этой серии. Давайте посмотрим на полный код, и я объясню ключевые части одну за другой:
leaderboardWAMS.js
(function () { "use strict"; // Leaderboard Client for Windows Azure Mobile Services // by G. Andrew Duthie // This code is copyright © Microsoft Corporation, and licensed under the Microsoft Limited Public License (Ms-LPL). // All rights reserved. // Code is provided AS-IS with no warranties. var leaderboardClient, gameScoresTable, leaderboardList, playerName, gameName, diag; WinJS.Namespace.define("Leaderboard", { setPlayerName: setPlayerName, init: init, addWin: incrementWins, addLoss: incrementLosses, addTie: incrementTies, updateScore: updateHighScore, getTopTenScores: getTopTenScores, getTopTenWins: getTopTenWinLossTie, leaderboardList: leaderboardList }); // player and game are required for initialization function init(player, game) { return new WinJS.Promise( function (completed, error, progress) { if (player) { playerName = player; } else { error("Player Name is required for initialization"); } if (game) { gameName = game; } else { error("Game Name is required for initialization"); } // replace with your custom WAMS instance information leaderboardClient = new Microsoft.WindowsAzure.MobileServices.MobileServiceClient( "https://YOURAPPNAME.azure-mobile.net/", "YOUR WINDOWS AZURE MOBILE SERVICE APPLICATION KEY" ); // Replace "gamescore" with your table name, if different gameScoresTable = leaderboardClient.getTable('gamescore'); // make sure that the score table has at least one record for the player name / game name combo gameScoresTable.where({ player: playerName, game: gameName }) .read().done( function (results) { if (results.length == 0) { // initialize table var gameScore = { game: gameName, player: playerName, score: 0, wins: 0, losses: 0, ties: 0 }; gameScoresTable.insert(gameScore); }; completed(); }, function (e) { error(e); }); }); } function getCurrentPlayerScore() { return new WinJS.Promise( function (completed, error, progress) { gameScoresTable.where({ player: playerName, game: gameName }) .read().done( function (results) { completed(results[0]); }, function (e) { // handle exceptions error(e); }); }); } function setPlayerName(name) { var namePromise = getCurrentPlayerScore(); namePromise.done(function (currentScore) { if (playerName != name) { playerName = name; currentScore.player = playerName; gameScoresTable.update(currentScore); } }, function (e) { showMessage(e.message); }); } function incrementWins() { var scorePromise = getCurrentPlayerScore(); scorePromise.done(function (currentScore) { currentScore.wins++; gameScoresTable.update(currentScore); }, function (e) { showMessage(e.message); }); } function incrementLosses() { var scorePromise = getCurrentPlayerScore(); scorePromise.done(function (currentScore) { currentScore.losses++; gameScoresTable.update(currentScore); }, function (e) { showMessage(e.message); }); } function incrementTies() { var scorePromise = getCurrentPlayerScore(); scorePromise.done(function (currentScore) { currentScore.ties++; gameScoresTable.update(currentScore); }, function (e) { showMessage(e.message); }); } function updateHighScore(newScore) { var scorePromise = getCurrentPlayerScore(); scorePromise.done(function (currentScore) { if (currentScore.score <= newScore) { currentScore.score = newScore; gameScoresTable.update(currentScore).done(function () { showMessage("Leaderboard Updated."); }); } }, function (e) { showMessage(e.message); }); } function getTopTenScores() { return new WinJS.Promise( function (completed, error, progress) { gameScoresTable .select("player", "score") .where({ game: gameName }) .orderByDescending("score") .read().done( function (results) { leaderboardList = new WinJS.Binding.List(results); completed(leaderboardList); }, function (e) { // handle exceptions error(e); }); }); } function getTopTenWinLossTie() { return new WinJS.Promise( function (completed, error, progress) { gameScoresTable .select("player", "wins", "losses", "ties") .where({ game: gameName }) .orderByDescending("wins") .read().done( function (results) { leaderboardList = new WinJS.Binding.List(results); completed(leaderboardList); }, function (e) { // handle exceptions error(e); }); }); } function showMessage(msg) { diag = new Windows.UI.Popups.MessageDialog(msg); diag.showAsync(); } })();
Вот важные части кода:
- Строки 12-22: как и в других примерах, здесь я определяю внешний API для вызова кода списка лидеров. Поскольку весь код в файле обернут анонимной самовыполняющейся функцией, внешний мир не может увидеть ничего, что не было бы открыто показано.
- Строка 43. Этот код, являющийся частью функции init, инициализирует объект leaderboardClient, используя URL-адрес мобильной службы вместе с ключом приложения, используя конструктор объекта MobileServiceClient, предоставляемый клиентской библиотекой Windows Azure Mobile Services JavaScript. Обратите внимание, что вам нужно указать свой собственный URL и ключ приложения.
- Строка 48-64: в этом коде, также являющемся частью функции init, я получаю ссылку на таблицу Gamescore, созданную ранее, а затем запрашиваю таблицу, используя интуитивно понятные функции .where и .read. Аргументы, которые я передаю функции where, ограничивают данные, возвращаемые только этой игрой и игроком. Обратите внимание на функцию .done, которая указывает, что .read возвращает объект WinJS.Promise, что имеет смысл, поскольку эта операция должна быть асинхронной. Когда возвращается функция .read, я проверяю, были ли возвращены какие-либо записи. Если нет, то я создаю объект, содержащий базовую схему для моей записи gamescore, и использую функцию .insert ссылки на объект таблицы, чтобы добавить ее в таблицу gamescore, а затем вызываю завершенную функцию, чтобы сообщить вызывающей функции init, что мы готово
Когда эта часть моего кода будет выполнена хотя бы один раз, я увижу следующее, если я посмотрю на вкладку Столбцы для моей таблицы игровых очков на портале управления:
Так что здесь произошло? По сути, клиентская библиотека, на которую ссылается мое приложение, упаковала данные как JSON ( которая на самом деле не требует никакой работы, поскольку я определил ее таким образом, во-первых … но хорошо иметь в виду, что даже если вы используете управляемая библиотека в приложении C #, данные транспортируются как JSON), а затем вызвал соответствующий REST URL для вставки новой записи в таблицу. Поскольку таблица не имела определенной схемы, мобильные службы проверяли данные JSON и определяли столбцы, способные поддерживать предоставленные данные. Это довольно круто, так как позволяет мне гибко определять мою схему на лету в моем коде. Однако следует помнить о том, что если в вашем коде есть опечатка, мобильные службы с таким же успехом будут создавать столбец, используя имя столбца с ошибкой, что может привести к некоторой забавной отладке. По этой причине мобильные службы позволяют отключить функцию динамической схемы, если вы выберете. Возможно, вы захотите оставить динамическую схему изначально, пока вы определяете свою схему, а затем отключите ее, чтобы избежать случайного переопределения схемы.
ОК, вернемся к коду:
- Строка 72: этот код, аналогичный коду в функции init, вызывает функции .where и .read объекта таблицы для получения уникальных записей для этого игрока и игры (должна быть только одна запись для каждого игрока / игровой комбинации) и возвращает Полученный объект вызывается функцией, используя функцию WinJS.Promise.done.
- Строки 84, 97, 107 и 117: в каждой из этих функций я вызываю функцию getCurrentPlayerScore, которая возвращает запись игрового счета для текущего игрока / игры, как описано выше, и в зависимости от того, в какую игру я играю, обновляет таблица игровых очков (по порядку) с новым именем игрока увеличивает столбец выигрышей, столбец потерь или столбец связей. В каждом случае, если что-то идет не так, я вызываю вспомогательную функцию для отображения сообщения пользователю.
- Строка 127: В функции updateHighScore я снова вызываю getCurrentPlayerScore, а затем проверяю, меньше ли результат в возвращенной записи, чем новый результат только что законченной игры. Если это так, тогда я устанавливаю для свойства Score объекта currentScore новое значение и вызываю функцию .update объекта таблицы для отправки обновленных данных в службу. В этом случае я также отображаю сообщение об успешном завершении, используя тот факт, что метод .update также возвращает обещание.
- Строки 141 и 160: эти две функции, которые используются на странице списка лидеров, используют некоторые из богатых функций запросов объекта таблицы, которые позволяют мне создавать несколько LINQ-подобных запросов, чтобы выбрать только те столбцы, которые я хочу для Для данной цели (.select) ограничьте объем возвращаемых данных (.where) и упорядочите данные (.orderByDescending). Вы можете прочитать больше о доступных функциях здесь . Получив необходимые результаты, я просто передаю их в конструктор объекта WinJS.Binding.List и передаю полученный список обратно вызывающей стороне, которая будет использовать его для связывания данных в элемент управления ListView на страница списка лидеров.
Это действительно все, что нужно для моего файла JavaScript.
Чтобы установить окончательное соединение с самой игрой, мне просто нужно обновить ссылку на скрипт на моих страницах default.html и leaderboard.html, например:
<script src="/js/leaderboardWAMS.js"></script>
Мне также нужно сослаться на файл MobileServicesJavaScriptClient / MobileServices.js (обратите внимание, что эта ссылка должна быть ДО ссылки на мой скрипт выше):
<script src="/MobileServicesJavaScriptClient/MobileServices.js"></script>
В случае страницы списка лидеров мне также нужно убедиться, что свойства привязки данных в моем шаблоне соответствуют именам столбцов в моей таблице, поскольку они чувствительны к регистру (строки 4 и 7 ниже):
<div id="myTemplate" data-win-control="WinJS.Binding.Template"> <div> <div class="win-type-x-large" style="width: 400px;"> <span>Player: </span><em><span data-win-bind="textContent: player"></span></em> </div> <div class="win-type-large"> <span>Score: </span><em><span data-win-bind="textContent: score"></span></em> </div> </div> </div>
Поскольку функции, представленные в моем пространстве имен Leaderboard, такие же, как и в предыдущих примерах, нет необходимости вообще обновлять игровую логику. После того, как я обновил ссылку на скрипт, я могу просто запустить игру, и когда игра закончится, если мой счет превысит существующий, я увижу следующее:
И когда я захожу на страницу лидеров, вот что я вижу:
Конечно, так как я только что создал свою таблицу, есть только одна запись, но приятно видеть, что она работает нормально.
Для корректности, после обновления кода для игры Catapult Wars и игры в пару игр, вот как выглядят данные моей таблицы игровых очков на портале управления:
Заворачивать
В этом посте я показал вам, как легко начать работу с Windows Azure Mobile Services. Создание новой мобильной службы и таблицы выполняется быстро и просто, а клиентские библиотеки предоставляют быстрый и интуитивно понятный способ подключения ваших приложений к мобильной службе, не беспокоясь о вызовах REST.
Фактически, просто переключив мою службу лидеров на Windows Azure Mobile Services, я смог уменьшить размер библиотеки JavaScript в таблице лидеров почти на 30%. Это значительное сокращение кода, почти все из-за более простого кода, необходимого благодаря клиентской библиотеке.
Но самое интересное в том, что все это просто RESTful-сервисы под прикрытием. Поэтому, если я создавал приложение на платформе, для которой Windows Azure Mobile Services еще не предоставляет клиентскую библиотеку (например, Android или даже веб-сайт), пока эта платформа может выполнять HTTP-вызовы, я все еще могу использовать мобильные услуги в качестве бэк-энда. Вы можете прочитать все о REST API Windows Azure Mobile Services здесь .
Вот преимущества и недостатки использования Windows Azure Mobile Services для этого вида услуг, на мой взгляд:
преимущества
- Быстрая разработка — настройка мобильной службы и таблиц данных занимает совсем немного времени, а благодаря клиентским библиотекам и возможности динамической схемы вы можете легко запустить и запустить базовую службу и подключиться к вашему приложению за считанные минуты.
- Интеграция. В дополнение к серверным данным и службам REST, рассмотренным в этом посте, мобильные службы также могут обеспечивать аутентификацию для различных поставщиков удостоверений (на момент написания этой статьи, учетных записей Microsoft, учетных записей Google, Twitter и Facebook), а также как отправлять push-уведомления. Я буду исследовать эти функции в следующих постах.
- Гибкость запросов — клиентские библиотеки мобильных сервисов предоставляют интуитивно понятные функции запросов, упрощая фильтрацию и сортировку данных. Как я показал выше, клиентская библиотека JavaScript делает это с помощью цепных вызовов функций.
- Масштабируемость. Службы Windows Azure Mobile Services позволяют начать с малого (и недорого) в режиме общих служб, а в период предварительного просмотра можно бесплатно получить до 10 служб в общем режиме, все из которых работают в одном экземпляре базы данных SQL. Это позволяет снизить затраты, но когда спрос на вашу услугу растет, вы можете перейти в режим «Зарезервировано», и ваша служба будет работать с несколькими экземплярами для поддержки многих пользователей.
Недостатки
- Служба предварительного просмотра. На момент написания этой статьи службы Windows Azure Mobile находились в режиме предварительного просмотра. Это означает, что части платформы все еще находятся в разработке, и SLA не предлагается на период предварительного просмотра.
- Ограниченная поддержка проверки на стороне сервера. Одна функция, которую я не успел подробно обсудить, — это поддержка мобильными службами сценариев на стороне сервера (с использованием JavaScript, выполняющегося в node.js). Для каждой операции в каждой таблице ваш мобильный сервис может выполнять код JavaScript. Это полезно, когда вы хотите предпринять определенные действия. Например, когда новая запись вставлена в таблицу, вы можете отправить push-уведомление. Вы также можете использовать эти функции на стороне сервера, чтобы обеспечить некоторую базовую проверку ввода, но на момент написания этой статьи это требует от вас написания всего кода для этой проверки. Это область, в которой некоторые из других технологий, которые я обсуждал, имеют преимущество, заключающееся в том, что во многих случаях вы можете просто украсить свои классы модели соответствующими атрибутами метаданных, и код клиента выполнит проверку для вас.
Если вы хотите тратить большую часть времени на разработку, ориентированную на клиентскую часть, Windows Azure Mobile Services — это платформа, которую вы обязательно должны рассмотреть. Лично я считаю, что это отличный способ быстро создать бэкэнд для моего приложения. Абстрагируя основную сложность построения интерфейса RESTful поверх таблиц данных и предоставляя простые в использовании клиентские библиотеки, мобильные сервисы помогают вам быстро и эффективно запускать сервис и без изучения технологий, таких как WCF Data Services или ASP.NET Web API.
В конечном счете, какая из этих трех технологий лучше для вас, будет зависеть от вашего опыта и того, на чем вы хотите сосредоточить свое время.
Для обзора
Если вы еще этого не сделали, я настоятельно рекомендую прочитать остальные посты этой серии:
- Создание данных и служб для приложений Windows 8: обзор
- Создание внутренних данных и служб для приложений Windows 8: OData — часть 1
- Создание внутренних данных и служб для приложений Windows 8: OData — часть 2
- Создание внутренних данных и служб для приложений Windows 8: веб-API ASP.NET
- Создание внутренних данных и служб для приложений Windows 8: мобильные службы Windows Azure (этот пост)
Что дальше?
В следующих выпусках этой серии я расскажу о добавлении аутентификации и push-уведомлений в службу лидеров, используя мобильные службы Windows Azure . У меня будет выходной на рождественские каникулы, так что ожидайте продолжения сериала в начале января.
Пока вы ждете, почему бы не зарегистрироваться на Generation App ? Существует множество отличных ресурсов для создания приложений для Windows 8 (а теперь и для Windows Phone 8), включая новую информацию от партнеров, которая позволяет быстро и легко создавать приложения и игры для Windows 8. бесплатно, и вы контролируете, как часто отправляются обновления, так что нет веских причин для их передачи. Зарегистрируйтесь сейчас !