Для выступления в Сумах обновил презентацию по SignalR. Если кому интересно, то можно посмотреть здесь:
об информационных технологиях, программировании, путешествиях и фотографии
Wednesday, December 5, 2012
Sunday, November 18, 2012
Разработка real-time ASP.NET приложений с помощью SignalR
SignalR, на мой взгляд – одна из самых впечатляющих библиотек в ASP.NET, появившихся за последние пару лет. Я бы сказал, настоящее событие, своеобразная веха. И пока команда корпит над готовящимся релизом первой версии, самое время сделать ее небольшое описание. Однако прежде чем начать, стоит немного остановиться на том, зачем вообще кто-то придумывал эту библиотеку и какую проблему она решает.
Real-time веб приложения
Наверняка многие пользовались Facebook, Twitter или другими социальными сервисами и замечали, что когда приходят обновления новостей или статусов, страница обновляется в реальном времени и вы постоянно видите последнюю актуальную информацию. В Google Docs можно совместно редактировать документы, и все обновления синхронизированы и появляются у вас сразу же, как только их сделает другой пользователь. Это примеры сервисов, которые можно назвать real-time веб-приложениями. Основной их отличительной способностью является то, что они “ломают” привычную нам всем модель работы в вебе – запрос-ответ, благодаря чему пользователи видят обновление данных сразу же, как только они появляются на сервере.
Если не знать, насколько далеко вперед шагнули технологии за последние несколько лет, то можно предположить, что все подобные приложения реализованы при помощи периодического опрашивания сервера обычными Ajax-запросами, т.н. polling. Выглядит похоже, но это не так, в чем можно легко убедиться, просмотрев логи запросов из браузера на сервер в Firebug, Fiddler или Chrome developer tools.
Способы реализации
Итак, какие же есть способы реализации подобной функциональности, их достоинства и недостатки.
Техника | Описание | Преимущества | Недостатки |
Polling | Постоянный опрос сервера Ajax-запросами | + простота реализации + поддержка во всех современных браузерах | - задержка в результатах - при уменьшении задержки существенно увеличивается нагрузка на сервер |
Long Polling | Ajax-запросы, идущие один за другим, но каждый запрос держится открытым в течение нескольких минут | + сниженная нагрузка на сервер по сравнению с обычным Polling + уменьшенный трафик + поддержка во всех современных браузерах | - больше одновременно открытых соединений, т.к. каждый запрос живет дольше |
Server-Sent Events | Новый стандарт HTML5, работающий поверх HTTP. Позволяет создавать долгоживущее соединение с сервером, чтобы сервер мог отправлять данные на клиент | + нет необходимости постоянно пересоединяться с сервером + нет изменений на стороне сервера, поэтому работает на всех современных веб-серверах | - не поддерживается в IE (даже в IE10) - работает только в направлении сервер –> клиент (на сервер можно отправлять обычные Ajax запросы) |
WebSockets | Новый протокол (ws:// и wss://), работающий поверх TCP на одном уровне с HTTP. Позволяет создавать двустороннее долгоживущее соединение с клиентом | + нет необходимости постоянно пересоединяться с сервером + работает в двустороннем режиме | - поддерживается не во всех веб-серверах (IIS8) - поддерживается не во всех браузерах (в IE7-9, Android) |
Для большей информации о Server-Sent Events и WebSockets советую посмотреть статью на HTML5 Rocks: http://www.html5rocks.com/en/tutorials/eventsource/basics/
Если посмотреть на достоинства и недостатки, то можно увидеть, что самый эффективный вариант – это WebSockets, но он не поддерживается во всех браузерах и будет поддерживаться лишь на IIS8 и выше. Server-Sent Events работает на более старых версиях IIS, но не поддерживается в IE, поэтому тоже подойдет далеко не всегда. Long Polling работает везде, но при этом далеко не так эффективен.
Идеальным решением было бы совмещение этих техник в разных случаях, но это сложно реализовать и поддерживать. Однако есть хорошая новость – это решение уже реализовано в библиотеке SignalR.
Введение в SignalR
SignalR – это библиотека для создания многопользовательских real-time ASP.NET (и не только) приложений. Она состоит из набора серверных и клиентских библиотек, и представляет собой абстракцию над целым набором транспортов. Все это добро – open source, лежащий на GitHub: https://github.com/SignalR, поэтому вы всегда можете пойти и посмотреть, что там внутри, какие есть баги и т.д. Кроме того, в разделе Wiki есть много информации о SignalR и примеров: https://github.com/SignalR/SignalR/wiki
SignalR был придуман и реализован двумя разработчиками Microsoft: Damian Edwards и David Fowler. В своих твиттерах они часто пишут полезную информацию о SignalR и сообщают о новостях. Также David ведет блог, в котором описывает все изменения в новых версиях. Если вы решите использовать библиотеку, то советую подписаться.
На момент написания этой статьи SignalR находится в предрелизном состоянии (версия 1.0 alpha2). Еще планируются один или несколько RC, после чего продукт будет выпущен в RTM.
Транспорты
Как я уже писал, SignalR – это абстракция над набором транспортов. Когда пользователь открывает страницу, работающую с SignalR, браузер опрашивает сервер на предмет поддержки разных транспортов и затем пытается подсоединиться по самому оптимальному для данного случая транспорту. Приоритет выглядит следующим образом:
То есть если сервер и клиент поддерживают WebSockets, то будет установлено WebSockets-соединение и все будут счастливы. Если же нет – то далее будет проверка, поддерживает ли клиент SSE, и если да – будет установлено это соединение. В случае IE сразу же будет испробован подход Forever Frame (невидимо висящий iframe устанавливает соединение и получает JS-инструкции с сервера) – это IE-хак, т.к. даже 10-я версия IE не поддерживает SSE. Молодцы, нечего сказать.
И в конце-концов, если клиент не поддерживает ни один из этих способов, или произошла ошибка, то SignalR откатится до самого надежного способа – Long Polling, который работает практически везде.
Архитектура
Архитектура SignalR очень проста. На сервере реализованы 2 вида API: низкоуровневый (PersistentConnection API) и высокоуровневый (Hub API), причем Hub опирается на PersistentConnection. Вы можете использовать любой из них, но в большинстве случаев вам будет достаточно возможностей, предоставляемых Hub API.
В альфе официально поддерживаются JavaScript (браузер), .NET 4 и WinRT клиенты. Windows Phone и Silverlight были в предыдущей версии, но в альфу не попали. Их обещают допилить ближе к релизу.
Также в альфу не попала библиотека для self hosting. Вместо нее обещают поддержку http://owin.org/.
Простое SignalR приложение
В качестве простого примера напишем приложение, которое позволяет нескольким пользователям двигать один и тот же объект в браузере таким образом, что изменения положения сразу же видны у других пользователей, открывших ту же страницу. В нашем случае объектом будет серый квадрат, но в целом это может быть что угодно.
1) Открываем VS и создаем ASP.NET MVC приложение
2) Инсталлируем пакет Microsoft.AspNet.SignalR через NuGet:
Install-Package Microsoft.AspNet.SignalR
Для того, чтобы команда сработала сейчас, до релиза, нужно добавить в конец префикс –pre. Со временем он будет не нужен.
После инсталляции вы увидите несколько изменений в проекте:
- В References добавятся библиотеки SignalR
- В Scripts добавятся библиотеки jQuery.SignalR
- В папке App_Start появится класс RegisterHubs, который занимается регистрацией рута ~/signalr/hubs
3) Создаем класс MoveHub в папке Controllers (может быть и другая папка, например, Hubs, но хабы – тоже своеобразные контроллеры):
using Microsoft.AspNet.SignalR.Hubs; namespace SignalR_test.Controllers { public class MoveHub : Hub { public void MoveShape(int x, int y) { Clients.All.shapeMoved(Context.ConnectionId, x, y); } } }
Обращение Clients.All обозначает, что мы хотим отправить сообщение всем клиентам, которые подписались на события, происходящие в хабе. Метод MoveShape будет вызываться с клиента и вызывать метод shapeMoved во всех браузерах, открывших страницу с параметрами x, y (положение квадрата) и Context.ConnectionId (уникальный идентификатор клиента, отправившего запрос на сервер). Объект Clients.All (и другие) – dynamic, поэтому мы можем вызывать любые методы с любыми параметрами без опасения ошибок компиляции. В то же время это обозначает и то, что нам нужно быть предельно внимательными с именами методов и параметров, включая регистр – никакой проверки компиляции здесь нет.
4) Добавляем метод контроллера, который будет отображать нашу страничку Shape.cshtml:
public class HomeController : Controller { ... public ActionResult Shape() { return View(); } }
Здесь все просто, комментировать нечего.
5) Добавляем Shape.cshtml view, в котором ссылаемся на несколько JS файлов.
Сначала подключаем jQuery и jQuery.UI (для реализации draggable), затем библиотеку jQuery.SignalR, и в конце – обращение к JS-файлу с хабами. Это то место, где происходит вся магия SignalR. Файл генерируется на лету инфраструктурой библиотеки на основании всех хабов, которые есть в вашем коде. Если заглянуть в этот файл из браузера, то можно увидеть, как в нем регистрируются объекты и методы, использующиеся впоследствии из JavaScript-кода.
6) Кладем во view кусок JS-кода. В настоящем приложении JS-код лучше вынести в отдельный файл, но для наших целей и такого кода достаточно.
В JavaScript мы делаем следующее:
- Регистрируем метод shapeMoved, который будет “вызываться” из серверного кода. В методе двигаем фигуру, если мы сами не являемся источником запроса
- Стартуем соединение с сервером
- По успешному подключению регистрируем обработчик события draggable у фигуры, который будет вызывать серверный метод moveShape, находящийся в нашем хабе
7) Запускаем приложение, открываем страницу в двух разных браузерах (или на двух разных машинах), и смотрим, как при движении фигуры в одном из браузеров, она автоматически двигается в другом.
Группы
Очень полезной возможностью SignalR является поддержка групп. Группы используются для объединения подключений и последующего их группового использования. Например, когда у вас есть разные объекты (комнаты чатов, аукционы и т.д.) и необходимо, чтобы оповещения получали лишь те пользователи, которые просматривают именно этот объект.
С группами очень просто работать:
// Add connection to group "foo" Groups.Add(Context.ConnectionId, "foo"); // Call send on everyone in group "foo" Clients.Group("foo").send(message); // Call send on everyone else but the caller in group "foo" Clients.OthersInGroup("foo").send(message); // Call send on everyone in "foo" excluding the specified connection ids Clients.Group("foo", Context.ConnectionId).send(message);
Другие возможности SignalR
В целом, SignalR реализует следующие сценарии:
- клиент вызывает метод на сервере
- сервер вызывает метод на клиенте/клиентах
- передача состояния с клиента на сервер и обратно
- поддержка передачи сложных объектов (JSON сериализация)
- определение соединения, отсоединения и пересоединения клиентов
- обращение к клиентам извне хаба при помощи специального интерфейса (то есть любой код на сервере может оповестить клиенты о событии)
- асинхронные сценарии
В официальной документации можно посмотреть более подробное описание всех API и их возможности, как на сервере, так и на клиентах:
Веб-ферма
Внимательные читатели уже поняли один небольшой подвох SignalR: для того, чтобы знать, кого оповещать, не говоря уже о том, кто в какой группе состоит, SignalR должен где-то хранить список подключений и групп. По умолчанию, он делает это в памяти, и это решение отлично подходит для одного сервера. Однако, если у нас веб-ферма, то механизм работать не будет: каждый сервер будет знать лишь о своих подключениях и никакой бродкаст на все подключения или группу будет невозможен.
Разработчики позаботились об этом и на данном этапе предлагают 3 решения:
- Поддержка Windows Azure Service Bus (идеально подходит для развертывания в Azure)
- Поддержка Service Bus for Windows Server
- Поддержка Redis
Если у вас не Azure, то остается два последних варианта. Из них лично я бы посоветовал Redis, т.к. он очень прост в установке и конфигурировании (его почти нет), в то время как Service Bus for Windows Server потребует от вас недюженных усилий.
Redis – это key-value storage, изначально разработанный для Linux, но под Windows существует несколько портов. Официально поддерживающийся порт и его настройка описаны в документации SignalR. Неофициальный, но по всей видимости более взрослый порт можно найти здесь: https://github.com/dmajkic/redis. Решение, как запустить его как Windows Service, описано здесь: https://github.com/kcherenkov/redis-windows-service.
Со временем обещают поддержку и других шин.
Полезные ссылки
Если вы хотите посмотреть пример приложения на SignalR, то вы можете скачать пример приложения, показывающего обновляющиеся биржевые котировки с GitHub (https://github.com/SignalR/SignalR-StockTicker), либо зайти на http://jabbr.net и попробовать вживую пример, реализующий чат. Кстати, на Jabbr в комнате, посвященной SignalR довольно часто бывает David Fowler, и у него можно спросить любой вопрос, который вас интересует.
- http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR – демонстрация SignalR на Channel 9
- http://channel9.msdn.com/Events/Build/2012/3-034 – видео доклада о SignalR с конференции Build
- http://www.hanselman.com/blog/CategoryView.aspx?category=SignalR – посты Скотта Хенселмана о Redis
- http://www.asp.net/vnext/overview/signalr/signalr-and-web-sockets – слайдкаст Скотта Хенселмана о Redis
- http://www.dotnetrocks.com/default.aspx?showNum=723 - подкаст .NET Rocks с авторами SignalR
- http://weblogs.asp.net/davidfowler/archive/2012/11/11/microsoft-asp-net-signalr.aspx – изменения в SignalR 1.0.0 по сравнению с предыдущими версиями
- http://www.slideshare.net/AlexMerle/realtime-aspnet-with-signalr – слайды моей презентации SignalR на конференции NETwork
Спасибо за внимание и успешной разработки!
Monday, December 26, 2011
Распределенные транзакции (Distributed Transactions) и их настройка
Каждый программист, работающий с данными, сталкивался с обычными транзакциями той или иной базы данных. Основная задача транзакций – обеспечить consistency данных после завершения операции: изменения либо успешно сохраняются от начала и до конца, либо полностью откатываются, если что-то пошло не так. Даже если вы ни разу не писали в SQL коде ключевые слова BEGIN TRAN, COMMIT TRAN или ROLLBACK TRAN или нечто подобное, это еще не значит, что вы их не использовали. Все ORM, реализующие паттерн unit of work (Entity Framework, NHibernate и др.) объединяют операции по изменению данных в транзакцию перед сохранением.
Транзакции хорошо работают в рамках одной базы данных, но если у вас распределенная система, которая требует сохранения данных в разных базах на разных серверах, а иногда и платформах, то нужна более тяжелая артиллерия – распределенные транзакции (distributed transactions, DT).
Перед тем, как начать работать с распределенными транзакциями, нужно изучить механизм их работы, протоколы и особенности, которые не так просты. Очень хорошее описание темы есть в этой статье, где дается отличное общее понимание и практические примеры, просто must read:
Truly Understanding .NET Transactions and WCF Implementation
Работа вложенных транзакций отлично расписана здесь:
Understanding nested transaction scopes
Также не забывайте про MSDN:
http://msdn.microsoft.com/en-us/library/w97s6fw4(v=VS.90).aspx
Итак, .NET поддерживает распределенные транзакции при помощи класса TransactionScope. Кроме этого, основная технология разработки распределенных приложений, WCF, также поддерживает распределенные транзакции из коробки, предоставляя программисту целый комплекс конфигурационных параметров и атрибутов, которые позволяют с легкостью превратить ваш сервис из обычного в распределенно-транзакционный.
Для того, чтобы запустить ваш сервис в режиме поддержки распределенных транзакций, необходимо выполнить несколько шагов, щедро описанных в книгах, а также статьях и блогах в Интернете:
1) Сконфигурировать WCF для поддержки DT
Распределенные транзакции не поддерживаются в режиме basicHttpBinding, поэтому нам нужно использовать хотя бы на wsHttpBinding, в binding которого нужно прописать атрибут transactionFlow=”true”:
<wshttpbinding> <binding name="wsConfig" transactionflow="true"> <security mode="None" /> </binding> </wshttpbinding>
2) Установить специальные атрибуты в интерфейсе сервиса и его методов
Необходимо добавить атрибуты TransactionFlow для метода в контракте и свойства атрибута OperationBehavior TransactionScopeRequired и опционально TransactionAutoComplete в реализации метода:
[ServiceContract] public interface IServiceContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] string ServiceMethod(string param1, string param2); } public class Service : IServiceContract { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] public string ServiceMethod(string param1, string param2) { // some server-side operations with database } }
Атрибут TransactionFlow принимает несколько опций: Allowed обозначает, что метод сервиса может вызываться как из кода, обернутого в TransactionScope, так и из обычного. Mandatory требует наличия TransactionScope, а NotAllowed (по умолчанию) заставит сервис игнорировать транзакции на клиенте вообще.
3) Создать на стороне клиента транзакцию, внутри которой вызвать метод WCF сервиса
Выглядит это приблизительно так:
public static string DoSomethingWithRemoteCall(string param1, string param2) { string result = null; TransactionOptions options = new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TimeSpan.FromSeconds(300) }; using (TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, options)) { // some client-side database actions using (var ecommerceFactory = new ChannelFactory("Staging")) { ecommerceFactory.Open(); var proxy = ecommerceFactory.CreateChannel(); bool success = false; try { result = proxy.ServiceMethod(param1, param2); success = true; } catch (Exception ex) { // log exception return; } finally { if (success) ecommerceFactory.Close(); else ecommerceFactory.Abort(); } } // other possible client-side database actions scope.Complete(); } return result; }
Безусловно, в вашем коде вызов сервиса должен находиться в отдельном классе, это лишь пример.
TransactionOptions позволяет задать некоторые параметры транзакции. В нашем случае это уровень изоляции (значение по умолчанию Serializable не рекомендуется из-за опасности возникновения дедлоков) и таймаут операции (5 минут).
При создании TransactionScope мы указываем параметр TransactionScopeOption.RequiresNew, что не позволит никакому другому коду обернуть наш метод в свою транзакцию. Подробнее вложенные транзакции описаны здесь.
Внутри TransactionScope в случае, если мы действительно хотим закоммитить транзакцию, мы делаем вызов scope.Complete(). Если нам нужно транзакцию откатить (как в случае с catch в примере), мы просто не вызываем Complete(). Вызывать Complete() нужно после всех клиентских операций с базой данных, которые происходят внутри транзакции, иначе у вас случится ошибка, что connection или provider уже закрыт.
Обратите внимание, что в коде сервиса из предыдущего пункта нет никакого намека на TransactionScope, кроме атрибутов TransactionFlow и OperationBehavior. Он там и не нужен, для стандартного сценария атрибутов достаточно. Однако никто вам не мешает создавать свои вложенные транзакции, как с опцией Required (используем родительскую транзакцию), так и с опциями RequiresNew (новая независимая транзакция) и Suppress (код не будет выполнятся в родительской транзакции).
4) Запустить сервис и клиент
И вуаля – все работает. Или не работает? Говорите, полезли странные ошибки выполнения?
Для того, чтобы распределенные транзакции заработали, необходимо сделать еще правильно сконфигурировать клиенты и сервера:
1) Убедиться, что на всех клиентах и серверах (здесь и далее - включая сервера баз данных и веб-сервера) установлена и запущена служба Distribution Transactions Coordinator. Именно эта служба отвечает за координацию ваших распределенных транзакций.
2) Убедиться, что на всех клиентах и серверах включена поддержка распределенных транзакций. Для этого запускаем Control Panel –> Administrative Tools –> Component Services, идем в Computers –> My Computer –> Distributed Transaction Coordinator –> Properties (контекстное меню) и устанавливаем на вкладке Security следующие параметры:
- Network DTC Access
- Allow Remote Clients
- Allow Inbound
- Allow Outbound
3) Разрешить работу Distributed Transactions Coordinator во всех установленных брандмауэрах, включая Windows Firewall:
4) Убедиться, что все ваши клиенты и сервера находятся в одной локальной сети. В большинстве случаев так оно и есть, но есть исключения, и если вы тот самый счастливчик, то вам придется немного попотеть, реализовывая поддержку протокола WS-Atomic Transaction (WS-AT), который упоминается в общей статье. Если вы тот самый счастливчик, которому надо начинать настраивать WS-AT, то вот еще пара полезных статей:
Configuring WS-Atomic Transaction support (MSDN)
5) Для работы DTC по локальной сети: все машины должны пинговаться по netbios-имени.
6) Важно: Если вы пошли по нашему пути и запустили тестовую конфигурацию на виртуальных машинах, запущенных с одного образа: переустановить DTC. DTC не работает с одинаковыми CID, а переустановка их сбрасывает. Это проблема, с которой мы столкнулись и которую смогли найти лишь запустив утилиту DTCPing.
Больше деталей здесь: Warning: the CID values for both test machines are the same
7) Если ничего не помогло: поставить и запустить DTCPing и посмотреть, что она говорит. Очень хороший способ, когда ничего другое не помогает:
Saturday, December 3, 2011
Paypal: работа с 3rd party merchant accounts
Интеграция с Paypal – вещь не сложная. Paypal поддерживает два типа API: Name-Value pairs (URL-based) и стандартный SOAP, а также несколько сервисов для работы, из которых самые распространенные Website Payments Standard и Website Payments Pro (Express Checkout и Direct Payments). Paypal также предоставлят Sandbox для тестирования и достаточно подробную документацию по каждому продукту и типу интеграции. Это если вкратце.
На деле же, как и с любым другим API, здесь есть свои подводные камни, с которыми приходится бодаться. Об одном из них, на который я потратил довольно много времени, мне и хотелось бы рассказать.
Для того, чтобы ecommerce сайт работал с Paypal, необходимо иметь Paypal merchant аккаунт, на который идет вся оплата за товары и услуги, продаваемые на сайте. Для авторизации на Paypal для аккаунта генерируются т.н. API Credentials: Username, Password и Signature/Certificate (лучше использовать Signature). Как это делается, в деталях рассказывается в документации. То есть механизм простой: веб-приложение передает запросы на Paypal, используя три параметра API Credentials, а Paypal таким образом “знает”, с каким merchant аккаунтом идет работа, грубо говоря, кому перечислять деньги за товары.
Все это замечательно работает, когда у вас в приложении “хостится” один продавец, и так работает наверно 95% всех ecommerce-сайтов, от простейших шаблонных электронных магазинов до более серьезных решений. Но в случае реализации сложной торговой площадки, на которой может работать множество торговцев, вроде Amazon.com, eBay.com или etsy.com, этого становится недостаточно. Есть два способа решения этой задачи.
Первый способ очевидный: пусть каждый merchant сгенерирует себе API Credentials и укажет их в своем профиле торговой площадки. Тогда при работе с выбранным Paypal API нужно будет всего лишь подставлять нужные credentials – и вуаля. Однако если посмотреть на профили того же Amazon или eBay для торговцев, то можно увидеть, что они просят ввести только Merchant account email. И никаких паролей или API credentials! Это и есть второй способ интеграции, не так хорошо задокументированный.
Упоминание об этом способе почему-то достаточно хорошо скрывается в документации к функциям API и фактически встречается лишь в описании работы с Name-Value pairs API и на форумах (кстати, форумы Paypal очень полезны, но гуглом не индексируются, поэтому лучше искать прямо там). Вот оно:
То есть можно сгенерировать API Credentials для всего приложения и динамически подменять лишь SUBJECT поле для каждого запроса, таким образом говоря Paypal, с каким merchant сейчас идет работа. И все бы хорошо, да только NOTE там указан не просто так. Paypal API достаточно мощный и дает возможность обращаться к некоторой внутренней информации merchant аккаунта, поэтому необходимо, чтобы merchant дал разрешение на определенные операции. Эти разрешения merchant может задать в Profile –> API Access –> Grant API Permissions. Далее нужно ввести API account username торговой площадки, которой даются разрешения и в появившемся списке выбрать те разрешения, которые нужны. Например, для работы с Express Checkout нужно выбрать как минимум:
- Use Express Checkout to process payments
Также полезными могут оказаться:
- Authorize and capture your PayPal transactions – если оплата проходит в 2 этапа: авторизация и захват/списание денег
- Refund a transaction on your behalf – для возврата денег в случае возврата товара
- и некоторые другие (полный список есть здесь)
Дальше – больше. Процесс разрешения прав можно упростить для торговцев еще сильнее, если использовать т.н. Paypal Permissions Service. Этот сервис предоставляет возможность интеграции торговой площадки со специальными страницами разрешения прав на Paypal. То есть пользователю-торговцу достаточно лишь кликнуть ссылку или кнопку на сайте торговой площадки, и он будет автоматически переброшен на страницу Paypal с уже выбранным набором прав для разрешения, необходимых вашему приложению. Пользователю останется лишь подтвердить разрешение.
Тестирование этого процесса также заслуживает внимания, так как там притаилась еще одна небольшая засада. Вам необходимо создать 2 (!) различных sandbox-аккаунта, один из которых будет использоваться для создания API Credentials веб-приложения, а второй – для создания тестовых merchant-аккаунтов, которые будут якобы продавать товары. И неважно, что merchant (или seller) аккаунты можно создать и на основном sandbox аккаунте, вам не удастся сконфигурировать нужные права и Paypal будет возвращать непонятные ошибки.
Надеюсь, для кого-то эта информация окажется полезной и сэкономит время.
Saturday, November 19, 2011
Онлайн-курсы от Стэнфорда
Многие программерские ресурсы уже раструбили эту новость, но, может, кто-то еще не слышал. Полтора месяца назад Стэнфордский университет запустил бесплатные онлайн-курсы по Artificial Intelligence и Machine Learning. Эксперимент оказался довольно удачным (хотя отзывы по курсу AI и хуже, чем по ML) и с января по март запускается вторая волна курсов, 8 9 по Computer Science и 2 по Enterpreneurship:
1. Machine Learning — jan2012.ml-class.org
2. Introduction to Computer Science — www.cs101-class.org
3. Natural Language Processing — www.nlp-class.org
4. SaaS and Software Engineering — www.saas-class.org
5. Human-Computer Interaction — www.hci-class.org
6. Game Theory — www.game-theory-class.org
7. Probabilistic Graphical Models — www.pgm-class.org
8. Cryptography — www.crypto-class.org
9. Design and Analysis of Algorithms — http://www.algo-class.org/
10. Technology Entrepreneurship — www.entrepreneur-class.org
11. The Lean Launchpad — www.launchpad-class.org
Курсы проводят в основном преподаватели Стэнфорда, многие из которых являются признанными экспертами в своих областях и авторами книг.
Курсы можно проходить в одном из двух режимов: Basic или Advanced track. В Basic треке вы просматриваете лекции и отвечаете на еженедельные тесты. При этом никто ничего от вас не требует. В Advanced треке в дополнение нужно делать лабораторные работы, которые будут проверяться преподавателями. Работы оцениваются, их нужно сдавать вовремя, за опоздание начисляются штрафные баллы. В конце курса в случае Advanced трека будут подведены итоги, и вы получите письмо от преподавателя с вашими результатами. Никаких сертификатов и прочего – все знания, что успели ухватить – все ваше.
По отзывам, на каждый курс уходит не менее нескольких часов в неделю, в зависимости от выбранного трека. Треки можно менять в процессе обучения, но нужно помнить про штрафные баллы.
Я решил записаться на 3 курса: Machine Learning, Natural Language Processing и Game Theory. По первому очень хорошие отзывы, да и просто любопытно сравнить с тем, чему нас учили, вторым я немного занимался в университете, а третий просто интересно прослушать – на нашей специальности (ПОАС) теории игр не было, но встречаюсь с ней все чаще. Не уверен, что хватит времени пройти все по Advanced треку, но во-первых, всегда можно перейти на Basic или вообще отказаться, а во-вторых, я думаю начать проходить Machine Learning сейчас с первой группой, включая лабораторные работы, чтобы в январе-марте сэкономить себе время на этом курсе.
В общем, я считаю, что это действительно круто! Дистанционное образование, о котором так долго твердили большевики, становится реальностью. Наверняка в ближайшем будущем еще несколько серьезных университетов разразятся новыми курсами.
PS. Кстати, кому интересно, до середины декабря проходит AI Challenge. В этом году, к сожалению, совсем нет времени участвовать, а жаль.
PPS. Курсы добавляются как грибы после дождя. Появился еще один курс, на этот раз по дизайну и анализу алгоритмов: http://www.algo-class.org/
Friday, May 20, 2011
Компании + вузы = исследования?
Только что прочел новость о том, что в ХНУРЭ открыли научно-учебную лабораторию по IT-технологиям. Особенно доставляет несколько моментов:
- лабораторию открыли при непосредственном участии EPAM Systems
- лабораторию называют “научно-учебной” :)
- проректор по научной работе ХНУРЭ говорит (просто на камеру?), что открытие лаборатории позволит улучшить уровень исследовательской работы университета
По первому пункту никаких вопросов нет. Компьютерные классы/курсы уже лет 10 открывают в харьковских университетах многие крупные компании города, которым нужен приток новых кадров. Зачем далеко ходить, я попал на свое первое место работы – в Validio - по схожей программе. Кризис закончился, на рынке снова дефицит кадров. Компании, которые сотрудничают с университетами, могут быть более уверены в завтрашнем дне.
Смешно другое. Каким боком это все к исследованиям и научной работе? Это ведь обычный компьютерный класс + программа обучения студентов, направленная на то, чтобы они знали востребованные на рынке языки программирования и технологии и их уровень адаптации на месте работы был минимальным. Аутсорсинговой компании, которая организовывает подобные курсы, не интересны исследования и научная работа. Об интересах студентов, компаний и вузов я писал 2.5 года назад, но все это до сих пор актуально.
Более того, уверен, что все в ХНУРЭ и других вузах понимают лучше меня, что лучшие студенты, которые уходят на подобные курсы, по сути, потеряны для исследований и научной работы. Их свободное время сначала будет уходить на курсы, потом они пойдут на практику в компанию, потом уйдут туда работать – и все. И произойдет это даже не на 5м курсе, а на 3-4м. Учеба (почти) закончится, диплом будет написан абы как, в аспирантуру студент не пойдет, потому что возможности там заработать нет.
Самое неприятное во всем этом – это то, что в данной ситуации ничего с этим поделать нельзя. Это как раз та модель, когда довольны остаются все стороны: студент получает высокоплачиваемую работу, компании – сотрудников, вуз – хм, в общем, он тоже не остается в накладе. Но это все краткосрочная перспектива. В долгосрочной перспективе студент не получает некоторые фундаментальные CS-знания и исследовательский опыт, предпочитая им изучение инструментов, вуз – талантливых студентов, способных заниматься наукой под руководством преподавателей и аспирантов, компании же не теряют почти ничего, их задачи такие “выпускники” покрывают, серьезных же R&D компаний в Украине нет или почти нет. Две “проигравшие” в долгосрочной перспективе стороны – студенты и вузы – либо не понимают этого (студенты), либо просто живут сегодняшним днем, предпочитая синицу в руке (вузы).
И ситуация не изменится пока в Украине не появится серьезный спрос на исследовательскую работу в области Computer Science и возможность платить за эти услуги. Только тогда вузы смогут удерживать у себя студентов хотя бы на период обучения, давая им другой, не промышленный опыт и знания, в рамках вузов начнут рождаться интересные разработки и стартапы, и курс нашей страны на IT рынке, возможно, сменится с аутсорсинга/аутстаффинга на разработку собственных продуктов и серьезные наукоёмкие исследования.
Sunday, May 15, 2011
Прогноз развития рынка разработки ПО
Disclaimer. Ни один из “прогнозов” не претендует на оригинальность. Скорее это просто наблюдения и попытка их аппроксимировать на ближайшие 5 лет.
1. Плавный переход в веб
Десктоп-приложения ползут в веб уже давно и этот процесс , судя по всему, будет лишь продолжаться. Веб-приложения кроссплатформенны, проще в установке, обновлении и поддержке, доступны бОльшему количеству пользователей. Чем сильнее будут развиваться веб-технологии (браузеры, HTML, RIA-технологии), тем больше десктоп приложений будут менять прописку, и тем более солидными и функциональными они будут.
Конечно, есть классы приложений, которые будут до последнего “жить” на компьютере пользователя: требующие большой вычислительной мощности, интерактивности и серьезной графической поддержки, инструментарии разработчиков, а также различные утилиты. Однако как только будет предложен более полный доступ браузерных приложений к системе и системным ресурсам с учетом угроз безопасности, которые возникают при этом, начнется новый виток эммиграции.
2. Браузер – единственное приложение или Терминалы наносят ответный удар
Наверно, самое часто используемое приложение на домашнем компьютере/лаптопе/нетбуке (нужное подчеркнуть) - браузер. Музыку можно слушать из инета, фильмы, информация, почта, офисные приложения, социальные сети и другие развлечения – тоже там. Google уловил эту тенденцию первым и уже предложил свое решение – Chrome OS. По сути, ОС с единственным пользовательским приложением – браузером. Сейчас эта идея выглядит немного утопично (ведь сколько еще приложений не в вебе!), но в перспективе 5-10 лет, думаю, мы увидим возрождение идеи терминалов в новой форме.
Корпоративный сектор тоже может себе позволить такой переход. Практически все типы приложения можно реализовать как веб интранет-приложения, и получать к ним доступ из локальной сети. Такой подход дешевле.
3. Развитие “облачных” технологий.
Благодаря своей специфике облака удобны для стартапов, интернет-приложений и сервисов с изменяющейся нагрузкой. В то же время и более консервативный корпоративный сектор все больше и больше движется в эту сторону. “Свои” приложения переводят в облако пока еще очень немногие, но покупать облачные сервисы стали все больше и больше. Это удобно – нет затрат на свою инфраструктуру, надежно, есть поддержка. Остаются лишь вопросы отказоустойчивости, безопасности данных и интеллектуальной собственности, но не для всех они актуальны.
Крупные продуктовые компании вовсю копают это направление. “Облачные” сервисы есть у Google (Gmail, Docs, etc.), Microsoft (Exchange, Office 365, Dynamics CRM, etc.), и многих других.
Кроме того, все больше и больше развиваются облачные платформы и для custom-решений. И это не только Amazon EC2, Google App Engine, Microsoft Azure. Очень много хостеров сейчас предлагают услуги облачного хостинга на своих платформах.
4. Значительное увеличение доли мобильных приложений
Мобильный сектор растет очень большими темпами. Телефоны, смартфоны, планшеты разлетаются по всему миру как горячие пирожки. Все идет к тому, что мобильные устройства до известной меры заменят ПК, а для домашнего использования будет проще использовать обычный нетбук или планшет.
Но речь не о ПК и их будущем, а о росте количества мобильных устройств, а следовательно, количества приложений, а следовательно, объема этого рынка и возможности на нем заработать.
Чем более мощными будут становиться устройства и их операционные системы, тем более “крутые” приложения будут портироваться на них. Думаю, сейчас аутсорсинговым компаниям очень выгодно открывать отделы “мобильной разработки”, заказы есть уже сейчас, и их количество обещает только расти. Про продуктовые компании и говорить нечего.
5. Смещение акцента в разработке мобильных приложений с native в web
Идея проста. Мобильных платформ все больше, доля на рынке так или иначе будет отъедаться даже у текущих лидеров: Apple OS и Android. Если вы хотите, чтобы ваше приложение было доступно не 30% “мобильных” пользователей, а хотя бы 60%, то извольте создать версию своего продукта и под другие платформы. А это очень недешево, технологии и языки программирования везде абсолютно разные, следовательно, создавать новое приложение нужно почти с нуля. А потом еще и поддерживать их все. С веб-приложениями проще – реализация на сервере одна, подстраиваемся под размер экрана и, возможно, меняем немного внешний вид – и готово. С обновлениями тоже все просто. Дешево и сердито. Из плюсов также возможности продажи по подписке, показа рекламы – все это дополнительный заработок.
Понятно, что не все native приложения можно перенести в веб. Приложения, которым требуется высокая производительность или по-настоящему интерактивный и “богатый” интерфейс, еще долгое время будут создаваться только как native-приложения. В то же время с приходом HTML5 возможности веб-приложений еще больше увеличились.
Понятно также, что не все производители приложений вообще стремятся перейти на другую мобильную платформу. Но уже сейчас на рынке есть 2 крупных игрока, хотя бы под них многие будут стараться писать. А ведь еще несколько (RIM, WP7) отдадут все силы, чтобы вмешаться в дележ этого некислого пирога.
6. Уменьшение доли custom решений за счет создания более универсальных продуктов
Сразу оговорюсь, что речь только о корпоративном (или государственном) секторе. Пользователи не заказывают разработку продуктов на заказ.
Сложно говорить о каких-то цифрах, их у меня нет. Но, думаю, многие согласятся, что если раньше в разных сферах было много custom решений, призванных решить конкретные нужды конкретного бизнеса или предприятия, то со временем появились универсальные продукты, решающие нужды целого класса таких бизнесов. Примеры: бухгалтерия, CRM, ERP системы, системы обслуживания банков, CMS-ки, eCommerce-платформы, портальные решения (привет, Sharepoint) и т.д.
Велик ли рынок разработки custom решений? Насколько я знаю, да. Будет ли он уменьшаться? Похоже на то. Купить продукт обычно дешевле, чем заказать разработку такого же. Рынок custom-решений живет за счет задач, для которых либо еще нет универсальных продуктов, либо никогда и не будет из-за их специфики.
7. Робототехника и интеллектуальные системы
Можно сказать, эта сфера лежит за пределами широкого рынка разработки ПО. Ею занимаются университеты, исследовательские центры, и инновационные компании. Такие проекты практически невозможно встретить в “дикой” природе, в них есть интеллектуальная собственность (IP), отдельные алгоритмы и модули часто патентуются.
Однако, чем дальше, тем больше это направление будет развиваться. Правда, будет ли это происходить в Украине – большой вопрос. Зависит от нас с вами.
А что Вы думаете по этому поводу?
Sunday, March 13, 2011
Bing Maps на Windows Phone 7. Полные исходники
Я планировал сделать целую серию заметок о создании простого Bing Maps приложения на Windows Phone 7, но жизнь вносит свои коррективы. Большое количество работы, сдача сертификации Microsoft, а также планирование отпуска отбирают все свободное время. К тому же, судя по отсутствию комментариев, эта тема никого особо не трогает. Поэтому эта серия останется незавершенной, но я все-таки хочу выложить все исходники проекта – может, кому-то пригодятся для самостоятельного изучения.
Исходники лежат на Google Code. Вот ссылка на папку Downloads проекта: http://code.google.com/p/wp7-wikimapia-client/downloads/list.
Если будут вопросы по коду или теме в целом – обращайтесь.
Tuesday, February 1, 2011
Bing Maps на Windows Phone 7. Часть 2. Масштабирование карты
Первая часть серии была посвящена созданию простейшего Bing Maps приложения для Windows Phone 7. Во второй части мы рассмотрим улучшение контролов изменения масштаба карты (zoom) и коснемся интерфейса INotifyPropertyChanged.
Изменение ViewModel
Может быть, вы заметили, что в прошлой части мы установили свойству ZoomBarVisibility значение Visible. Стандартный Zoom Bar выглядит достаточно грубо, кроме того, отображается в нижней части карты, что не очень удобно. Поэтому мы его заменим на наш собственный.
Для начала устанавливаем свойству ZoomBarVisibility значение Collapsed (или просто удаляем его из списка свойств).
Далее нам нужно расширить наш MapViewModel, добавив туда свойство Zoom, которое будет источником данных для свойства ZoomLevel у контрола Map. Добавляем в MapViewModel свойство и некоторые константы для удобства:
private const double DefaultZoomLevel = 4.0; private const double MaxZoomLevel = 21.0; private const double MinZoomLevel = 3.0;
public double Zoom { get { return zoom; } set { var coercedZoom = Math.Max(MinZoomLevel, Math.Min(MaxZoomLevel, value)); if (zoom != coercedZoom) { zoom = value; NotifyPropertyChanged("Zoom"); } } }
Здесь вроде бы все понятно за исключением NotifyPropertyChanged. NotifyPropertyChanged – это метод-реализация интерфейса INotifyPropertyChanged. Этот интерфейс используется для оповещения классов, которые привязываются (binding) к источнику данных, об изменениях в этом источнике. В нашем случае контрол Map нашей View будет таким образом оповещаться об изменениях в свойстве Zoom.
Теперь нужно, чтобы класс MapViewModel наследовался от интерфейса INotifyPropertyChanged. Можно это сделать напрямую, а можно избежать дублирования реализации интерфейса во всех классах моделей представления, создав базовый класс, который будет реализовывать интерфейс, и унаследовавшись от него.
Добавляем в папку ViewModel проекта новый класс BaseViewModel, который будет выглядеть следующим образом:
public class BaseViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = this.PropertyChanged; if (handler != null && !String.IsNullOrEmpty(propertyName)) { handler(this, new PropertyChangedEventArgs(propertyName)); } } }
И наследуем класс MapViewModel от BaseViewModel:
public class MapViewModel : BaseViewModel
Работа с представлением
Теперь нам нужно перейти во View и привязать свойство ZoomLevel контрола Map к свойству Zoom модели представления:
ZoomLevel="{Binding Zoom, Mode=TwoWay}"
Добавляем кнопки для масштабирования карты внутрь <canvas>:
<Button x:Name="ButtonZoomIn" BorderThickness="0" Margin="8,200,0,0" Padding="0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="72" Height="72" Click="ButtonZoomIn_Click"> <Image Source="Images/Menu/ZoomIn_White.png" /> </Button> <Button x:Name="ButtonZoomOut" BorderThickness="0" Margin="8,280,0,0" Padding="0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="72" Width="72" Click="ButtonZoomOut_Click"> <Image Source="Images/Menu/ZoomOut_White.png" /> </Button>
Соединяем все вместе
Теперь нжуно реализовать обработку нажатий на кнопки. Для этого добавляем обработчики событий для кнопок, которые будут изменять свойство Zoom модели представления, в code-behind файл MainPage.xaml.cs:
private void ButtonZoomIn_Click(object sender, RoutedEventArgs e) { Model.Zoom++; } private void ButtonZoomOut_Click(object sender, RoutedEventArgs e) { Model.Zoom--; }
Добавляем свойство Model в тот же code-behind файл:
protected MapViewModel Model { get { return (MapViewModel)Resources["ViewModel"]; } }
Это свойство сделано для удобства доступа к ViewModel, которая зарегистрирована как ресурс представления. Таким образом мы можем вызывать какие-нибудь методы или устанавливать свойства ViewModel.
Если говорить строго, то написание логики представления (а в нашем случае это обработка событий и устновка свойства Zoom) – это уже отход от правильной реализации паттерна MVVM. Но в то же время, code-behind страницы – это все еще View, поэтому мы можем сами решать, как оповещать наш ViewModel о событиях UI. Поэтому для начальной реализации приложения мы будем использовать code-behind страницы как прокси к ViewModel для упрощения реализации. В реальном же приложении нам нужно было бы использовать т.н. команды для привязки событий ко View, а не обычные обработчики. Но поскольку Silverlight до сих пор не обзавелся официальной поддержкой команд (за исключением интерфейса ICommand), а писать свои с нуля мы еще не готовы, то пока что отложим этот вопрос на будущее. Если кому-то интересно узнать больше уже сейчас, вот несколько ссылок:
http://weblogs.asp.net/nmarun/archive/2009/12/02/using-icommand-silverlight-4.aspx
http://johnpapa.net/silverlight/5-simple-steps-to-commanding-in-silverlight/
http://blogs.southworks.net/jdominguez/2008/08/icommand-for-silverlight-with-attached-behaviors/
http://houseofbilz.com/archives/2009/05/22/adventures-in-mvvm-commands-in-silverlight/
Добавляем картинки
Итак, логика готова. Нам осталось лишь добавить картинки для кнопок. Для этого создаем папку Images\Menu в проекте и добавляем туда файлы ZoomIn_White.png и ZoomOut_White.png. Скачать все файлы картинок, включая эти две, можно отсюда. Напоследок нам нужно открыть окно Properties для каждой из этих картинок и установить там свойство Build Action в Content вместо Resource. Этим мы убиваем двух зайцев: уменьшаем размер сборки, тем самым сокращая время загрузки приложения, и упрощаем доступ к файлам из приложения.
Запускаем приложение и видим, что у нас появились кнопки масштабирования. Их можно покликать – они работают :)
В следующей части мы реализуем получение нашего положения в пространстве при помощи GeoLocation API и научимся центрировать карту в это положение.
Вся серия:
Часть 1. Введение
Часть 2. Масштабирование карты
Часть 3. Полные исходники
Sunday, January 30, 2011
Bing Maps на Windows Phone 7. Часть 1. Введение
Эта статья содержит примеры кода, подсвеченные с помощью JS SyntaxHighlighter, который почему-то не отрабатывает в Google Reader, а возможно и в других RSS ридерах. Чтобы нормально увидеть код, вам придется перейти в блог. Извините за неудобство.
Недавно я делал code session на встрече харьковской UNETA на тему создания картографического приложения для Windows Phone 7. По моим субъективным ощущениям все прошло неплохо, слушатели помогали мне в написании кода, активно задавали вопросы и поправляли меня, когда я где-нибудь лажал :), а также стойко терпели технические проблемы, связанные со взаимной нелюбовью моего ноутбука и местного проектора, за что им отдельное спасибо. Так как тема программирования под Windows Phone сейчас интересна многим людям, хотелось бы также выложить материалы этого code session в блог.
Этой статьей я начинаю серию, посвященную созданию простенького картографического приложения на Silverlight с использованием Bing Maps. В процессе написания приложения я иногда буду уходить в теорию, чтобы пояснить те или иные детали. Итак, приступим.
Требования
Прежде чем начинать писать приложение, необходимо определиться что мы, собственно говоря, хотим написать. Наше приложение будет отображать текущее местонахождение человека, а также показывать положение различных объектов вокруг него: кафе, гостиниц, магазинов, банков, туристических мест, ночных клубов и прочего добра. По идее, оно должно помогать людям ориентироваться в малознакомом или даже родном городе. Если собрать все требования в кучку, то мы получим следующий набор функций приложения:
- отображение карты
- определение текущего местоположения, центрирование карты
- отображение различных объектов вокруг текущего местоположения
- отображение деталей объекта при клике на нем
- фильтрация объектов по типам
При этом мы попытаемся реализовать приложение в более-менее правильном стиле, с использованием паттерна MVVM.
Начнем с создания проекта и отображения простой карты.
Создание проекта
Перед тем, как начать программировать для Windows Phone 7, вам нужно убедиться, что у вас есть вся инфраструктура:
- Visual Studio 2010 (есть бесплатная версия VS2010 Express for Windows Phone)
- Windows Phone Developer Tools (Silverlight for Windows Phone, эмулятор, библиотеки и т.д.)
- Bing Maps Silverlight control for Windows Phone
- (опционально) XNA Game Studio 4.0
- (опционально) Expression Blend (есть бесплатная версия Blend for Windows Phone)
Все это добро бесплатно и его можно скачать с сайта http://create.msdn.com/en-us/home/getting_started. После того, как установите основной пакет, не забудьте также установить October 2010 update to the Windows Phone Developer Tools.
Вообще, конечно, радует, что Microsoft решила сделать все инструменты для разработки WP7 приложений бесплатными. Но это не подарок на Новый год, а простой маркетинг: нужно привлекать программистов на новую платформу, чтобы догнать уходящий поезд, на котором дружно едут iOS и Android.
Немного о платформе разработки
Как вы уже догадались, писать мы будем на Silverlight. Только не на обычном Silverlight 4, а на т.н. Silverlight for Windows Phone. Эта версия SL отличается от своего настольного брата немного урезанными возможностями и оптимизированной производительностью. В общем-то производительность для телефона – это все, потому что по сравнению с native-приложениями у нас и сам .NET довольно прожорливый, и тем более Silverlight.
Кроме программирования бизнес-приложений (и в более редких случаях игр) на SL, у нас также есть возможность программировать игры (и в более редких случаях бизнес-приложения) на XNA. XNA – это платформа и набор инструментов для разработки игр для Windows, Xbox, Zune и вот теперь еще Windows Phone 7.
Поддержка XNA в Windows Phone – это очень полезная вещь, т.к. разработчики игр под Xbox теперь могут использовать привычные инструменты и модель разработки для программирования игр под мобильную платформу, а также с меньшими трудозатратами перенести существующие игры на WP7.
Отображение карты
Вернемся к практике. После установки необходимых дополнений в Visual Studio появятся новые типы проектов. Поэтому идем в File -> New -> Project и создаем Windows Phone Application проект.
Начнем мы с отображения карты. Здесь все просто. Сначала добавим в проект reference на сборку Microsoft.Phone.Controls.Maps. Затем добавим namespace для Bing Maps контрола, а также сам контрол в XAML главной страницы приложения, попутно заменив основной Grid на Canvas, чтобы нам было проще позиционировать элементы в будущем:
xmlns:maps="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
<!--LayoutRoot is the root grid where all page content is placed--> <Canvas x:Name="LayoutRoot"> <Grid Background="Transparent" Height="768" Width="480"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel contains the name of the application and page title--> <TextBlock x:Name="ApplicationTitle" Grid.Row="0" Text="My Places" Style="{StaticResource PhoneTextNormalStyle}"/> <maps:Map Grid.Row="1" Name="mapPlace" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ScaleVisibility="Visible" ZoomBarVisibility="Visible" CopyrightVisibility="Collapsed"> <maps:Map.Mode> <maps:AerialMode ShouldDisplayLabels="True" /> </maps:Map.Mode> </maps:Map> </Grid> </Canvas>
Свойства контрола ScaleVisibility, ZoomBarVisibility и CopyrightVisibility определяют видимость соответствующих стандартных элементов карты. Надо отметить, что стандартный Zoom Bar располагается снизу (что не всегда удобно) + выглядит очень плохо, поэтому немного позже мы его улучшим.
Запустим приложение в эмуляторе. Как видите, приложение показывает карту:
Если присмотреться, то можно заметить, что посередине экрана белым шрифтом написан следующий текст:
Invalid Credentials. Sign up for for a developer account.
Этот текст обозначает, что нам нужно получить Bing Maps Developer Account и сообщить о нем контролу карты. Для этого идем на http://www.bingmapsportal.com/, логинимся туда при помощи Live ID (или сначала регистрируем его), и затем создаем ключ при помощи пункта меню Create or view keys.
Для того, чтобы карта заработала, достаточно установить свойство CredentialsProvider контрола:
CredentialsProvider="AoYOaLG9nor0LXJzvYrL18U8URccNTyacL-qfGUMBdvFseoLbF384hct4JxGVvI9"
Напомню, что ключ у вас должен быть свой. Это не важно для тестового проекта, но не уверен, что не будет проблем в случае публикации вашего приложения на MarketPlace.
Однако мы же пытаемся все делать правильно, используя паттерн MVVM, не так ли? Поэтому данный способ не совсем для нас. Этот ключ – данные, а данные должны связываться с View (представление, наша страница) через ViewModel (модель представления), специальный объект, содержащий весь набор данных, необходимых View для работы, а также логику представления. Выглядеть это должно где-то так:
Дополнительную информацию по паттерну MVVM и его примеры для Silverlight можно прочитать на страничке MSDN и в статье в MSDN Magazine.
Итак, нам нужно сделать ViewModel класс и как-то привязать его к View. Создадим в проекте папку ViewModel, в которой будут лежать все модели представления, а следом класс MapViewModel, который будет связываться с представлением нашей главной страницы). На данном этапе класс будет выглядеть следующим образом:
public class MapViewModel { private const string Id = "AoYOaLG9nor0LXJzvYrL18U8URccNTyacL-qfGUMBdvFseoLbF384hct4JxGVvI9"; private readonly CredentialsProvider credentialsProvider = new ApplicationIdCredentialsProvider(Id); public CredentialsProvider CredentialsProvider { get { return credentialsProvider; } } }
Как видите, наш ключ для Bing Maps прописан в нем как константа. Конечно, его можно было бы вынести и в какой-нибудь конфигурационный файл, но для нас это сейчас не принципиально.
Далее привязываем MapsViewModel к представлению в XAML файле страницы. Добавляем namespace:
xmlns:model="clr-namespace:WP7Test.ViewModel"
Говорим нашей View, что ее ViewModel будем класс MapsViewModel, установив свойство PhoneApplicationPage.Resources (перед началом тега Canvas):
<phone:PhoneApplicationPage.Resources> <model:MapViewModel x:Key="ViewModel" /> </phone:PhoneApplicationPage.Resources>
Устанавливаем свойство DataContext у Canvas:
<Canvas x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}">
Теперь, когда ViewModel привязан к представлению, можно связать установить свойство CredentialsProvider контрола Map через стандартный data binding:
CredentialsProvider="{Binding ViewModel.CredentialsProvider}"
Вот и все. Запускаем, и видим, что наше приложение больше не показывает белую строчку.
В следующей части мы рассмотрим кастомизацию Zoom Bar.
Вся серия:
Часть 1. Введение
Часть 2. Масштабирование карты
Часть 3. Полные исходники
Saturday, January 15, 2011
Создание картографического приложения на Silverlight для Windows Phone 7
В четверг, 20 января, буду делать доклад на встрече харьковской UNETA на вышеприведенную тему. Хотя это будет даже не доклад, а скорее code session с минимумом теории и максимумом программирования. Постараюсь за 40 минут показать, как создать картографическое приложение на Silverlight для Windows Phone 7. Приложение будет брать данные об объектах из Wikimapia по различным категориям и отображать их на карте (Bing Maps). Покажу как работать с geolocation services, обрабатывать различные состояния приложения, и многое другое. Постараюсь, чтобы все было true MVVM. Ну, или около того :) Отвечу на разные вопросы.
Встреча будет проходить в Харьковском национальном университете радиоэлектроники, пр. Ленина, 14 (ст.м. Научная) 334 ауд., 20 января 2011 г. в 18-30. Официальное оповещение: http://dev.net.ua/blogs/leshchinsky/archive/2011/01/14/UNETA2001201SilverlightWindowsPhone7.aspx
Пока не знаю, что получится, будет тестовый прогон нового формата. Надеюсь, будет не очень скучно :) Приходите.
Thursday, March 11, 2010
Слайдкаст по принципам проектирования
При помощи PowerPoint'а, пары программ по редактированию и конвертации звука и такой-то матери я наконец-то слепил свой первый слайдкаст и выложил его на Slideshare. Это слайдкаст с недавнего доклада по принципам проектирования и длится он аж три четверти часа, что сильно уменьшает его шансы быть прослушанным вами :). Вопросы-ответы в середине и конце пришлось выкинуть, т.к. вопросов не слышно - микрофон был только у меня.
Должен сказать, я надеялся, что это будет легче. Лепил я его в течении нескольких дней, сначала более-менее приведя в порядок звук (хотя некоторые мои мэканья, неверные стилистические обороты и дыхание кое-где пооставались, извините), потом вдоволь навоевавшись с конвертором презентаций Slideshare (было несколько проблем с нежеланием конвертировать и неверным отображением), и в заключение победив их не самый безбажный редактор слайдкастов. Но все-таки приятно, что на Slideshare есть возможность создавать слайдкасты - это действительно здорово.
В целом, первый блин вышел комом, но эксперимент с записью звука вместо видео все же удался. Надеюсь, будем и в будущем записывать выступления таким образом и выкладывать их.
Надеюсь, кому-то это все будет полезно, несмотря на несколько ошибок в содержании, которые я допустил.
Если вдруг слайдкаст не открывается из блога - вот прямая ссылка.
Принимаются любые пожелания по улучшению.
PS. И спасибо Вове Лещинскому за микрофон!
Sunday, January 17, 2010
Качественный код и проектирование
В процессе своего развития любое приложение становится все больше и сложнее, зачастую теряя при этом такие свои полезные свойства как управляемость и надежность. Программистам становится сложнее добавлять новый функционал, изменять старый при необходимости, исправлять ошибки и т.д. В таких случаях обычно говорят, что приложение становится менее управляемым и теряет свое внутреннее качество.
Под внутренним качеством подразумевается не качество самого приложения (внешнее качество), то есть отсутствие ошибок, соответствие требованиям, простоту использования и т.д., а качество кода этого самого приложения.
Свойства качественного кода
Какими же свойствами обладает качественный код:
- расширяемость, гибкость (extensibility, agility)
- сопровождаемость (maintainability)
- простота (simplicity)
- читабельность, понятность (readability, clarity)
- тестируемость (testability)
Конечно, это не все полезные свойства, которыми может обладать код, но мы пока остановимся на этих. Эти свойства без преувеличения можно назвать ценностями кода, потому что код, ими обладающий, является по-настоящему ценным :)
Примечание. Однако, стоит заметить, что некоторые ценности противоречат друг другу. Например, расширяемый код, как правило, более сложен в написании и, соответственно, понимании. Сложно написать код, который бы обладал всеми вышеперечисленными свойствами в полной мере, поэтому здесь всегда приходится идти на компромисс в зависимости от дополнительных требований конкретного приложения и, следовательно, его приоритетов.
Итак, теперь мы лучше понимаем, что означают абстрактные слова "хороший код", "правильный код" и т.д. Это код, который обладает полезными внутренними качествами (ценностями).
Как же достичь хорошего кода?
Существует довольно много способов, и их можно условно разделить на внешние и внутренние. Внешние способы - это различные программистские практики, например, парное программирование, статический анализ кода, code review и т.д. В какой-то степени сюда можно отнести и модульное тестирование с TDD/BDD, так как они улучшают качество кода: написать модульные тесты на существующий плохой код довольно сложно, поэтому его приходится рефакторить, а при использовании TDD/BDD написать плохой код с самого начала довольно сложно, хотя и вполне реально.
Внутренние способы намного прозаичнее - это правильное проектирование и рефакторинг кода как способ превращения плохого кода в более хороший.
Внешние способы достаточно результативны, однако они всегда являются лишь дополнением внутренних. Если программист не обладает навыками правильного написания кода и проектирования, парное программирование с более сильным товарищем или code review ему поможет. Но что делать, если они оба не обладают этими навыками? Поэтому для каждого программиста, кроме знания инструмента (языка программирования, технологий) важно также и умение ими пользоваться (я уже когда-то об этом писал). Это 2 разных, ортогональных друг другу направления развития.
Итак, рассмотрим один из внутренних навыков, а именно навык проектирования (design).
Зачем каждому программисту иметь навыки проектирования
Многие программисты думают, что проектирование - это какая-то специальная фаза при разработке ПО, считают ее сложной и не всегда нужной. Строят там какие-то заумные дяди какие-то UML-диаграмки и прочую чушь. Все равно ведь когда будем писать код - будем половину менять и переписывать. Но ведь на самом деле, проектирование - это не только формальная фаза процесса создания ПО (практически любого), а очень важный (я бы сказал даже необходимый) шаг в программировании. А если выкинуть название "проектирование", то это необходимый шаг в любой человеческой активности. Сейчас объясню на примере, почему. Представим себе, что перед нами стоит какая-то задача. Что мы должны сделать перед тем, как начать ее выполнять? По идее, сначала мы должны понять что нам нужно сделать. Вторым шагом нужно разобраться, как мы будем это делать. И только потом - уже собственно делать! Так и в создании ПО. Сначала мы пытаемся понять, что нужно сделать (анализируем требования, или иногда даже анализируем желания клиента и создаем требования по ним). Затем мы пытаемся разобраться как мы будем это делать (по сути, проектируем, как будет выглядеть результат, чтобы не переделывать десять раз). И только потом берем клавиатуру в зубы и идем программировать. Однако здесь важно понимать одну очень важную деталь: результат проектирования (проект) не обязательно должен быть вырезан в камне диаграммах UML и документах, а вполне может быть сделан на доске, куске бумажки или даже просто в голове, если этого достаточно. Так что отсутствие конкретной формальной фазы еще не значит, что программисты не занимаются проектированием какого-то куска кода, базы данных, UI прототипов или даже микро-архитектуры. Занимаются. Это и есть проектирование, просто менее формальное.
Таким образом, даже несмотря на то что формальное проектирование и отсутствует на определенных проектах, фактическое проектирование все равно происходит. Потому что все равно без него никуда. Однако зачастую это обозначает, что этим самым проектированием занимаются не только опытные программисты и архитекторы, а практически все программисты, что увеличивает ответственность каждого конкретного члена команды. Ведь из-за моего непрофессионализма могу пострадать не только я (баги, частый рефакторинг, и т.д.), но и вся команда и проект в целом. И вот тут становится понятно, что навыки проектирования так же важны для каждого программиста, как и знание и понимание языков программирования и технологий. Не говоря уже о том, что без этих навыков вы не сможете сами эффективно и качественно создавать большие системы в роли технического лидера проектной команды или архитектора.
Технический долг
С вопросом важности проектирования тесно связана концепция технического долга или задолженности (Technical debt). Технический долг - это очень хорошая метафора, иллюстрирующая то, что быстрая и бездумная разработка кода приводит к появлению т.н. технического долга, похожего на финансовый долг, когда мы берем кредит. Как и финансовый, технический долг (кредит) имеет свои проценты, которые накапливаются с течением времени, и которые мы постоянно вынуждены выплачивать при добавлении новой функциональности, если мы не хотим выплачивать тело этого кредита. Со временем проценты становятся настолько велики, что мы вынуждены остановить добавление функционала и выплатить часть тела нашего кредита - зарефакторить быстрый и кривой дизайн приложения и сделать его более качественным. Несмотря на то, что этот процесс занимает время, таким образом мы уменьшаем будущие проценты.
Также эта метафора объясняет, почему иногда приходится писать код быстро, не тратя времени на правильное проектирование. Как и с точки зрения финансов мы берем деньги в кредит/долг для более быстрого достижения поставленной цели, здесь мы это также делаем для более быстрого получения результата. Однако мораль сей басни такова, что не нужно забывать, что за подобные действия мы будем расплачиваться в дальнейшем.
Более подробно о техническом долге можно почитать у Фаулера и Jeff Atwood.
Рецепты правильного проектирования
Хорошо, теперь мы знаем, что есть свойства хорошего кода (ценности), и что проектирование может нам помочь достичь их. Но как это сделать? Какие есть рецепты?
Индустрия создания программного обеспечения очень молода. Создание ПО часто сравнивают со строительством, архитектурой, мол, там все четко и понятно - есть требования к зданию, по ним делается дизайн, проект, потом этот проект просчитывается, конструируется, подбираются материалы, все планируется и лишь только затем начинается постройка. Кроме того, на данный момент существует уже куча стандартных подходов для строительства различных типичных зданий, существуют различные инженерные решения, позволяющий построить как гигантский небоскреб, так и тоннель под Ла-Маншем или какой-нибудь супер-мост. Однако, почти все забывают одно существенное различие: архитектуре уже как минимум 10,000 лет и человечество накопило очень много знаний и опыта за это время, а вот индустрии создания ПО - меньше века. И если в сфере языков программирования и технологий наша индустрия проделала очень внушительный путь, то по проектированию накопленных знаний не так и много. Многие подходы еще только нарабатываются и внедряются, поэтому и готовых рецептов-то не так уж и много.
Из того, что связано непосредственно с проектированием, можно назвать паттерны и принципы проектирования.
Паттерны (шаблоны) проектирования - это типовые конструкции кода, представляющие собой решение какой-нибудь типовой задачи проектирования (с) Википедия. Кто-то их любит использовать, а кто-то считает, что мышление шаблонами заглушает творческий подход в разработке. Но ясно одно - паттерны представляют собой готовые крупные строительные блоки, из которых можно создать ваше приложение, и так или иначе знать о них и об анти-паттернах полезно.
Принципы проектирования - это определенные правила, помогающие более правильно проектировать программный продукт (определение корявое, потому что мое :). Принципы проектирования, в отличие от паттернов, не предлагают конкретного решения проблемы, а скорее говорят, как приблизительно должно выглядеть правильное решение или как его достичь. То есть, в какой-то мере, можно сказать, что принципы описывают пути достижения тех самых ценностей, а паттерны в свою очередь зачастую следуют тем или иным принципам, то есть стоят на ступеньку ниже них. Также, поскольку принципы не конкретизируют результат, они дают нам намного большесвободы по их применению.
Мне кажется, что ценности, принципы и паттерны можно выразить следующей иерархией (с удовольствием жду комментариев по ее улучшению):
На вершине пирамидки располаются наши ценности (свойства), которых мы хотим достичь. Чуть ниже располагаются принципы - в какой-то степени они описывают правила, используя которые мы можем достичь наших ценностей. Еще ниже - паттерны, как более низкоуровневые рецепты. И в самом низу - обыкновенный код.
Однако данная пирамидка совсем не обозначает, что нельзя достичь вершины, не зная те же паттерны или принципы. Очень даже можно, вот только не факт, что в полученном результате их не будет :) Да и зачем изобретать велосипед, если можно использовать наработки таких умных и известных инженеров, как Бек, Гамма, Фаулер, Лисков, и множество других. Понимание и разумное использование принципов и паттернов помогает достигнуть вершины быстрее и с меньшими трудозатратами.
Мне кажется, что информации по паттернам проектирования уже очень много, а вот с принципами все не так просто. Поэтому в следующих частях я постараюсь осветить принципы проектирования, и не останавливаться только на SOLID.
Monday, August 10, 2009
Новые возможности LINQ to SQL в .NET 4.0
Вдогонку к моей предыдущей заметке предлагаю посмотреть список новых возможностей LINQ to SQL в .NET 4.0. Переводить не буду, просто дам ссылку на заметку Damien Guard из Microsoft, который работает над этим продуктом:
LINQ to SQL changes in .NET 4.0
Приятно, что L2S тоже развивается, пусть и не такими семимильными шагами как EF, NH и некоторые другие ORM. Я уже когда-то писал на тему развития этих технологий, а именно почему EF больше повезло в плане развития (и, соответственно, финансирования). По всей видимости, L2S будет тихим сапом поддерживаться и понемногу развиваться еще пару версий, а потом просто остановится, когда EF станет достаточно взрослым, продвинутым и быстрым, чтобы удовлетворить требования разработчиков, использующих L2S вместо EF или NH.