Visual Studio

Используйте несколько типов проектов Visual Studio для создания успешных приложений в облаке

Патрик Фоли (Patrick Foley)

Загрузка примера кода

Как вы, вероятно, заметили, в Visual Studio предлагается много различных типов проектов. Какой из них выбрать? Все они имеют свои сильные стороны, которые проявляются в решении задач в разных ситуациях. Даже в рамках единственной бизнес-проблемы часто существует несколько сценариев применения, каждый из которых эффективнее решается своим типом проекта в Visual Studio.

Я недавно столкнулся с такой проблемой, создавая инфраструктуру для программы на основе облака, которой я руковожу; она предназначена для публикации историй успеха: Microsoft Solutions Advocates (microsoftsolutionsadvocates.com). Для создания решения я использовал несколько разных типов проектов Visual Studio и в этой статье представлю упрощенный пример своего проекта под названием «Customer Success Stories» (CSS).

В CSS три разных сценария применения.

  1. Анонимные пользователи читают истории успеха на общедоступном веб-сайте.
  2. Пользователи, участвующие в программе, входят на закрытый веб-сайт и создают или редактируют свои истории успеха.
  3. Администраторы (вроде меня) входят на административный сайт для редактирования и управления любыми данными, включая такие мелочи, как таблицы поиска (lookup tables).

Я выбрал комбинацию их трех технологий Microsoft .NET Framework.

  1. ASP.NET MVC для общедоступного сайта.
  2. WCF RIA Services для закрытого сайта.
  3. ASP.NET Dynamic Data для административного сайта.

Любую из этих технологий можно было бы задействовать для создания всего решения, но я предпочел использовать сильные стороны каждой из них. ASP.NET MVC идеальна для создания общедоступного веб-сайта, который будет работать везде, хотя бы потому, что генерирует стандартный HTML. Общедоступный сайт выполняет маркетинговые функции, поэтому на заключительной стадии разработки я привлек к работе дизайнера для шлифовки его внешнего вида. Работа при этом усложняется, но в ASP.NET MVC можно достаточно легко реализовать видение дизайнера. Сделав сайт только для чтения и отделив его от других сценариев применения, вы очерчиваете область, доступную дизайнеру для изменений.

Хотя ASP.NET MVC можно было бы использовать и для реализации функциональности, позволяющей клиентам редактировать информацию, в этом плане больше подходит технология WCF RIA Services. (И наоборот, с помощью WCF RIA Services можно было бы построить отлично выглядящий общедоступный веб-сайт, но Silverlight не поддерживается на некоторых устройствах, например на iPhone и iPad, а я хотел охватить максимально большую аудиторию.) Silverlight никуда не денется, и она великолепна в формировании богатой функциональности редактирования с минимальными усилиями в программировании, если, конечно, она установлена у пользователей, а именно так и обстоит дело в случае клиентов, которые вносят свой вклад в развитие сайта, предлагающего истории успеха.

ASP.NET Dynamic Data предоставляет удобный способ создания административного решения без чрезмерных усилий. Административному сайту незачем выглядеть броско — он просто должен обеспечивать управление всеми данными в решении, не прибегая к SQL Server Management Studio. Что касается моего решения, то сайт ASP.NET Dynamic Data мог бы быть создан на основе WCF RIA Services. Тем не менее эта технология полезна на начальном этапе такого проекта разработки, ориентированного на данные, как мое решение.

Разработка для Windows Azure

Еще раз повторю, что этот пример основан на практической проблеме и, поскольку решение требует наличия общедоступного веб-сайта, я буду ориентироваться на Windows Azure и SQL Azure. Возможно, Windows Server и SQL Server были бы привычнее, но мне нужны эксплуатационные преимущества облака (не требуется поддерживать ОС, применять исправления и т. д.). У меня очень мало времени на создание решения и уж точно нет времени на поддержку его эксплуатации, поэтому Windows Azure является идеальным выбором.

Чтобы проработать этот пример и опробовать его в Windows Azure, вам понадобиться учетная запись. Существуют различные варианты и пакеты, ознакомиться с которыми можно по ссылке microsoft.com/windowsazure/offers. Подписчики MSDN и партнеры Microsoft (в том числе начинающие предприятия по программе BizSpark; подробности см. по ссылке bizspark.com) получают бесплатный доступ к ресурсам на несколько месяцев. Для создания прототипов и обучения (в частности, для проработки этого примера) можно использовать Introductory Special. Этот вариант включает три месяца бесплатного использования базы данных SQL Azure размером до 1 Гб и 25 часов работы с маломасштабным экземпляром Windows Azure, чего вполне достаточно для ознакомления с платформой. Я создал этот пример, используя Introductory Special без всяких дополнительных расходов.

Обзор Customer Success Stories

Этот пример представлен в виде учебного пособия. Некоторые важные аспекты этой практической реализации выходят за рамки данной статьи, и здесь не рассматриваются такие вопросы, как защита на уровне пользователей, тестирование, работа с дизайнером и вообще что-либо, не укладывающееся в крайне упрощенную модель. Эти вопросы я постараюсь рассмотреть в будущих статьях, которые будут опубликованы в моем блоге pfoley.com.

От вас потребуется общее представление о Visual Studio и знание применяемых технологий. Полный исходный код решения можно скачать по ссылке code.msdn.microsoft.com/mag201101VSCloud, а пошаговые инструкции вы найдете на странице pfoley.com/mm2011jan.

Этап 1: создание проекта для модели данных Entity Framework

Все веб-технологии, применяемые в этом примере, эффективно используют Entity Framework, поэтому я предпочел интегрировать все три сценария применения на основе общей модели Entity Framework. При работе с существующей базой данных вы должны генерировать модель на основе этой базы данных. По возможности я старался сначала создавать модель и генерировать базу данных на ее основе, так как мне больше нравится продумывать проект на уровне модели, а не базы данных. Для сохранения простоты в примере используются всего две сущности: Company и Story.

Модель Entity Framework создается в своем проекте и совместно используется в остальных проектах (я научился этому у Джули Лерман (Julie Lerman); подробности см. по ссылке pfoley.com/mm2011jan01). Такие рекомендации я называю «тайными жестами» — разобраться в них с первого раза весьма трудно, но, как только понимаешь секрет, все очень просто.

  1. Создайте модель Entity Framework в проекте библиотеки классов.
  2. Скопируйте строку подключения во все проекты, где будет использоваться эта модель.
  3. Добавьте ссылку на проект Entity Framework и System.Data.Entity во все проекты, где будет использоваться эта модель.

Для начала создайте Blank Solution в Visual Studio 2010, присвойте ему название «Customer Success Stories» и добавьте проект Class Library с именем CSSModel. Удалите файл класса и добавьте пустой элемент ADO.NET Entity Data Model с именем CSSModel. Добавьте сущности Company и Story со связью между ними, как на рис. 1 (щелкнув правой кнопкой мыши сущность Company, чтобы добавить связь, убедитесь, что в появившемся диалоге выбран Add foreign key properties to ‘Person’ Entity; на следующих этапах потребуются свойства внешнего ключа).

image: Adding Company and Story Entities to the Visual Studio Project

Рис. 1. Добавление сущностей Company и Story в проект Visual Studio

Модель готова для генерации таблиц базы данных, но базе данных SQL Azure нужно их вставить.Когда прототип закончен и проект развивается, полезно добавить локальную базу данных SQL Server для тестирования, но на сегодняшний день проще работать напрямую с базой данных SQL Azure.

С портала SQL Azure под своей учетной записью создайте новую базу данных CSSDB, добавьте правила для вашего текущего IP-адреса и на вкладке Firewall Settings включите Allow Microsoft Services access to this server. Ваш портал SQL Azure должен выглядеть, как показано на рис. 2.

image: Configuring Settings in the SQL Azure Portal

Рис. 2. Настройка параметров на портале SQL Azure

В Visual Studio щелкните правой кнопкой мыши рабочую область дизайнера и выберите Generate Database from Model. Добавьте соединение в свою новую базу данных SQL Azure и запустите мастер, который генерирует кое-какой DDL-код (Data Definition Language) и открывает его в файле .sql, как показано на рис. 3.

image: Generating the Database Model

Рис. 3. Генерация базы данных по модели

Прежде чем вы сможете выполнять SQL, нужно подключиться к базе данных SQL Azure (щелкните кнопку Connect на панели инструментов Transact-SQL Editor). Выражение USE в SQL Azure не поддерживается, поэтому вы должны выбрать новую базу данных из раскрывающегося списка Database на панели инструментов, а затем выполнять SQL. Теперь у вас есть база данных SQL Azure, которую вы можете анализировать в Visual Studio Server Explorer, SQL Server Management Studio или в новом инструменте управления — проекте Microsoft под кодовым названием Houston (sqlazurelabs.com/houston.aspx). Кроме того, после сборки решения вы получаете проект Entity Framework, с помощью которого можно обращаться к этой базе данных программным путем.

Этап 2: создание проекта ASP.NET Dynamic Data

Веб-сайт ASP.NET Dynamic Data позволяет легко работать со всеми данными в базе данных и предоставляет базовую функциональность для проверки корректной работы среды — и все это одной строкой кода.

Добавьте в решение новый проект ASP.NET Dynamic Data Entities Web Application и назовите его CSSAdmin. Чтобы задействовать модель данных, созданную на первом этапе, скопируйте элемент connectionStrings из App.Config для CSSModel в web.config для CSSAdmin. Укажите CSSAdmin как стартовый проект и добавьте ссылки на проект CSSModel и System.Data.Entity.

В проектах ASP.NET Dynamic Data можно делать массу впечатляющих вещей, но самое удивительное, что можно реализовать поведение по умолчанию простым раскомментированием строки RegisterContext в Global.asax.cs и ее изменением на:

DefaultModel.RegisterContext(typeof(CSSModel.CSSModelContainer), new ContextConfiguration() { ScaffoldAllTables = true });

Скомпилируйте и запустите проект — вы получите базовый сайт для управления данными. Добавьте какие-нибудь тестовые данные, чтобы проверить, все ли в порядке.

Этап 3: создание проекта сервиса Windows Azure

Результат предыдущего этапа — локальный веб-сайт, который обращается к базе данных в SQL Azure.Следующий этап — добиться, чтобы этот веб-сайт работал в Windows Azure.

Зайдите на портал Windows Azure под своей учетной записью, создайте Storage Service с именем CSS Storage и Hosted Service с именем CSS Service. Портал Windows Azure должен выглядеть, как на рис. 4.

image: Creating Services in the Windows Azure Portal

Рис. 4. Создание сервисов на портале Windows Azure

В Visual Studio добавьте в решение новый проект Windows Azure Cloud Service и назовите его CSSAdminService (нужно установить Windows Azure Tools for Visual Studio), но не добавляйте дополнительные решения облачного сервиса (cloud service solutions) из мастера. Проект облачного сервиса добавляет инфраструктуру, необходимую для выполнения вашего приложения в локальной версии «облачной материи» с целью разработки и отладки. Она также облегчает интерактивную публикацию в Windows Azure. Это очень удобно для создания прототипов и несложных решений для Windows Azure, но, как только вы займетесь серьезной разработкой на платформе Windows Azure, вы наверняка предпочтете использовать Windows PowerShell для скриптового управления развертыванием.

Щелкните правой кнопкой мыши папку Roles в CSSAdminService и выберите Add | Web Role Project in solution для сопоставления проекта CSSAdmin с проектом облачного сервиса. После компиляции и запуска решение будет выполняться в инфраструктуре разработки. В этот момент решение ничем не будет отличаться от выполняемого в IIS или Cassini, но важно, чтобы оно работало именно в инфраструктуре разработки, — это позволит отлавливать ошибки вроде использования неподдерживаемых API-функций Windows в процессе создания решения для Windows Azure.

Выполните развертывание под учетной записью Windows Azure, щелкнув правой кнопкой мыши проект CSSAdminService и выбрав Publish. Делая это в первый раз, вам потребуется предоставить удостоверения защиты (следуйте инструкциям по копированию сертификата в вашу учетную запись Windows Azure). Далее выберите Hosted Service Slot и Storage Account для развертывания решения. Есть два варианта слота размещаемого сервиса (hosted service slot): производственный (production) и промежуточный (staging). При обновлении производственного решения сначала разверните его в промежуточной среде, чтобы убедиться в корректности его работы, а уже потом перенесите из промежуточной среды в производственную. Создавая прототип, я предпочитаю развертывать его прямо в производственной среде, поскольку у меня нет намерений оставлять это решение работающим в таком виде. Щелкните OK для развертывания в Windows Azure, что может занять несколько минут. По окончании запустите свое приложение Windows Azure, используя URL веб-сайта, показанный на странице сервиса (рис. 5).

image: A Windows Azure Deployed Service

Рис. 5. Развернутый сервис Windows Azure

Убедившись, что сервис работает, остановите его и отмените развертывание, чтобы избежать ненужных расходов (в планах на основе использования учитывается момент развертывания пакета сервиса независимо от того, выполняется он или нет). Не удаляйте сам сервис, так как иначе URL веб-сайта будет возвращен в пул доступных URL. Очевидно, когда вы будете готовы запустить реальное производственное решение, вам придется предусмотреть бюджет на безостановочное выполнение сервисов Windows Azure.

Если развертывание сервиса завершается неудачей, не всегда точно сообщается о причине. Обычно вообще не сообщается, что возникла какая-то ошибка. Состояние сервиса просто входит в петлю наподобие «Initializing… Busy… Stopping… Initializing…».Когда такое произойдет у вас — а рано или поздно так и будет, — ищите проблемы наподобие попыток обращения к локальным ресурсам (возможно, к локальной базе данных SQL Server) или ссылок на сборки, отсутствующие в Windows Azure. Включение IntelliTrace при развертывании пакета (рис. 6) может помочь в локализации причины проблем, так как тогда будут генерироваться соответствующие исключения.

image: Enabling IntelliTrace While Publishing to Windows Azure

Рис. 6. Включение IntelliTrace при публикации в Windows Azure

Этап 4: создание проекта ASP.NET MVC

На данный момент решение состоит из административного веб-сайта (хоть и без защиты на уровне пользователей), выполняемого в Windows Azure и обращающегося к базе данных SQL Azure; при этом потребовалась всего одна строка кода. Теперь нужно создать общедоступный веб-сайт.

Добавьте в решение новый проект ASP.NET MVC 2 Web Application с именем CSSPublic (не создавайте проект модульного тестирования, работая над этим примером). Если у вас есть опыт работы с ASP.NET MVC, то, вероятно, вы предпочтете начать с ASP.NET MVC 2 Empty Web Application, но я решил начать со структуры веб-сайта, которая уже работает, и постепенно модифицировать ее, чтобы добиться того, что мне нужно.

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

  1. Удалите div-тег logindisplay из Site.Master.
  2. Удалите строку подключения ApplicationServices и элемент authentication из основного файла Web.config.
  3. Удалите AccountController.cs, AccountModels.cs, LogOnUserControl.ascx и всю папку Account в Views.
  4. Снова запустите проект, чтобы убедиться, что он еще работает.
  5. Скопируйте строку подключения из CSSModel App.Config в CSSPublic Web.config и добавьте ссылки на CSSModel и System.Data.Entity (точно так же, как и раньше).
  6. Выберите все ссылки для CSSPublic и установите свойство Copy Local в true.

Я думаю, имеет смысл добавить отдельные контроллеры (с сопоставленными представлениями) для Companies и Stories, в то же время сохранив контроллер Home в качестве исходной страницы. Это важные решения; дизайнер может сделать сайт красивым, но об архитектуре информации — структуре сайта — нужно думать в первую очередь.

Именование уместно в проекте Model View Controller (MVC). Используйте имена контроллеров во множественном числе (Companies, а не Company) соответствующие папки представлений. В именах представлений и методов контроллера придерживайтесь стандартных соглашений: Index, Details и т. д. Вы можете изменять эти соглашения, если хотите, но это добавит вам сложностей.

Щелкните правой кнопкой мыши папку Controllers, чтобы добавить новый контроллер с именем CompaniesController. На этом сайте не реализуются поведения — он предназначен только для чтения, поэтому нет нужды в явной модели классов. Считайте моделью сам контейнер модели Entity Framework. В CompaniesController.cs добавьте выражение using CSSModel и измените метод Index так, чтобы он возвращал список компаний:

CSSModelContainer db = new CSSModelContainer();
return View(db.Companies.ToList());

Чтобы создать представление, создайте пустую папку Companies в Views и щелкните ее правой кнопкой мыши для добавления представления с именем Index. Сделайте его строго типизированным с помощью класса данных View из CSSModel.Company представления контента в виде списка (класс List).

В Site.Master добавьте элемент списка в меню для ссылки на новый Controller:

<li><%: Html.ActionLink("Companies", "Index", "Companies")%></li>

Запустите приложение и щелкните меню, чтобы увидеть список компаний. Представление по умолчанию — хорошая отправная точка, но удалите ненужное поле Id. Так как этот сайт доступен только для чтения, удалите записи ActionLink для Edit, Delete и Create. Наконец, сделайте само название компании ссылкой на представление Details:

<%: Html.ActionLink(item.Name, "Details", new { id=item.Id })%>

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

image: A List of Companies in ASP.NET MVC Web Site

Рис. 7. Список компаний на веб-сайте ASP.NET MVC

Чтобы реализовать Details, добавьте в CompaniesController метод:

public ActionResult Details(int id)
{
  CSSModelContainer db = new CSSModelContainer();
  return View(db.Companies.Single(c => c.Id.Equals(id)));
}

Параметр id представляет целое число в URL, следующее за Companies\Details\. Это число используется для поиска нужной компании с помощью простого LINQ-выражения.

Добавьте представление Details в папку Companies, как и раньше, но на этот раз выберите Details как контент представления (View content) и назовите это представление Details.

Запустите проект, перейдите к Companies, а затем щелкните одно из названий компаний, чтобы увидеть представление Details по умолчанию.

Добавление контроллера и представлений для Stories осуществляется аналогично. Представления по-прежнему потребуют приличной работы по настройке, прежде чем их можно будет передать дизайнеру, но это хорошая отправная точка, и отсюда их можно будет легко развивать.

Чтобы проверить работоспособность проекта в Windows Azure, создайте проект облачного сервиса с именем CSSPublicService и добавьте в него роль CSSPublic. Запустите сервис локально в инфраструктуре разработки, а затем опубликуйте сайт в Windows Azure и запустите его с общедоступного URL. Не забудьте потом (после проверки) остановить его работу и отменить развертывание, чтобы избежать лишних расходов.

Этап 5: создание проекта WCF RIA Services

В решении к этому моменту содержатся сайты ASP.NET MVC и ASP.NET Dynamic Data, выполняемые (или, по крайней мере, готовые к выполнению) в Windows Azure; при этом для доступа к базе данных SQL Azure используется общая модель Entity Framework. Для получения вполне приличной функциональности понадобилось написать совсем немного инфраструктурного кода. Добавление веб-сайта WCF RIA Services открывает другое измерение: богатые возможности редактирования.

Добавьте проект Silverlight Business Application с именем CSSCustomerEdit (без пробелов), и Visual Studio включит в ваше решение два проекта: клиента Silverlight (CSSCustomerEdit) и веб-сервиса (CSSCustomerEdit.Web). Запустите решение, чтобы понять, с чем вы начинаете. Откройте ApplicationStrings.resx в проекте CSSCustomerEdit и измените значение для ApplicationName на «Customer Success Stories».

В CSSCustomerEdit.Web скопируйте connectionStrings из CSSModel в Web.config, добавьте ссылки на CSSModel и System.Data.Entity и установите свойство Copy Local в true для всех ссылок. Затем щелкните правой кнопкой мыши папку Services и добавьте новый элемент Domain Service Class с именем CSSDomainService. Убедитесь, что имя этого класса оканчивается словом «Service» без номера — это позволит задействовать весь инструментарий между двумя проектами (еще один «тайный жест»). Щелкните OK, чтобы открыть диалог Add New Domain Service Class, и пометьте все сущности, попутно включив в каждой из них Enable editing (рис. 8).

image: Adding a Domain Service Class

Рис. 8. Диалог Add New Domain Service Class

Заметьте, что параметр Generate associated classes for metadata отображается серым цветом. Это расплата за подход, который я исповедую здесь. В Silverlight Business Application для добавления дополнительной логики проверки (например, диапазонов) и отображения значений по умолчанию можно использовать классы метаданных. Однако, когда модель Entity Framework находится в проекте, отдельном от CSSCustomerEdit.Web, инструментарий не позволяет добавлять эти классы метаданных. Если эта возможность важна для вас или если вы собираетесь вкладывать основные усилия в ту часть решения, которая относится к Silverlight Business Application, то, вероятно, вы предпочтете создать свою модель Entity Framework прямо в проекте «.Web», а не в отдельном проекте. При этом вы смогли бы ссылаться на CSSCustomerEdit.Web, чтобы модель Entity Framework была доступна в другом проекте.

Как упоминалось, аутентификация и авторизация выходят за рамки этой статьи, но, даже плывя на плоскодонке, можно сохранять равновесие. Добавьте в класс CSSDomainService свойство-заполнитель (placeholder property) с именем myCompany, представляющее компанию, информацию о которой разрешено изменять данному пользователю. Пока что присвойте ему прямо в коде значение 1, но в конечном счете оно будет определяться в процессе входа.

Отредактируйте класс CSSDomainService, чтобы отразить специфику сценария применения проекта: пользователи могут обновлять информацию о компаниях, но не вставлять новые компании или удалять существующие (это делает администратор на веб-сайте ASP.NET Dynamic Data), поэтому удалите эти методы сервиса. Кроме того, пользователь может редактировать информацию лишь об одной компании, на которую он работает, а значит, измените GetCompanies на GetMyCompany. Аналогично переименуйте GetStories в GetMyStories и убедитесь, что пользователь создает истории, CompanyId которых равен myCompany:

private int myCompany = 1; // TODO: set myCompany during authentication
public Company GetMyCompany()
{
  return this.ObjectContext.Companies.Single(c=>c.Id.Equals(myCompany));
}
...
public IQueryable<Story> GetMyStories()
{
  return this.ObjectContext.Stories.Where(s=>s.CompanyId.Equals(myCompany));
}

public void InsertStory(Story story)
{
  story.CompanyId = myCompany;
  story.Id = -1; // database will replace with next auto-increment value
  ...
}

WCF RIA Services великолепно подходит для создания интерфейсов, ориентированных на работу с полями и их редактирование, но важно начинать с простого и добавлять функциональность постепенно, а не всю сразу. Элементы управления DataGrid и DataForm обладают широкими возможностями, но всякий раз, когда я слишком тороплюсь или добавляю за один раз слишком много функциональности, дело кончается путаницей и вынужденным откатом назад. Так что лучше работать в итеративном стиле и единовременно добавлять по одной UI-функции.

Чтобы реализовать базовый UI для этого примера, добавьте ссылки на System.Windows.Controls.Data и System.Windows.Controls.DomainServices в CSSCustomerEdit. Создайте новые представления (элементы страницы Silverlight) для Company (в единственном числе) и Stories, затем напишите XAML как в существующих представлениях Home и About. Отредактируйте MainPage.xaml, чтобы добавить новые разделители (dividers) и кнопки-ссылки (в качестве альтернативы просто включите существующие представления Home и About для использования в Company и Stories).

В разработках на основе Silverlight основная работа выполняется редактированием XAML. В CSSCustomerEdit добавьте элементы пространства имен, DomainDataSource и DataForm для представления Company. Кроме того, добавьте DataGrid для представления Stories. В обоих DataForm обрабатывайте событие EditEnded для вызова MyData.SubmitChanges. В итоге Stories.xaml должен выглядеть аналогично показанному на рис. 9.

image: XAML for the Stories View

Рис. 9. XAML для представления Stories

Скомпилируйте… запустите… работает! А вот уже вариант с полнофункциональными средствами редактирования (рис. 10).

image: The Stories View in Action

Рис. 10. Представление Stories в действии

Как и раньше, создайте новый проект облачного сервиса, опубликуйте его и протестируйте в Windows Azure. Скопируйте CSSCustomerEditTestPage.aspx в Default.aspx, и на этом все.

«Единственно верного пути» нет

Visual Studio и .NET Framework дают колоссальный выбор при создании решений, способных работать в Windows Azure. Хотя у вас может возникнуть соблазн поискать «единственно верный путь» при разработке очередной революционной новинки для облака, куда большего успеха вы добьетесь, используя возможности множества технологий при решении комплексных задач. Так легче кодировать, развивать свое приложение и — благодаря Windows Azure — проще его эксплуатировать.

Патрик Фоли (Patrick Foley)  — архитектор-идеолог в Microsoft, работающий с независимыми поставщиками ПО (ISV).Помогает таким компаниям создавать успешные приложения на платформе Microsoft. Читайте его блог pfoley.com.

Выражаю благодарность за рецензирование статьи эксперту Хану Коммалапати (Hanu Kommalapati)