Все большее количество компаний внедряет решения Microsoft Dynamics CRM 4.0 и обнаруживает необходимость в создании внешних приложений, которые можно интегрировать с существующим API на основе веб-сервисов.
Создание приложений Silverlight для взаимодействия с Microsoft Dynamics CRM 4.0 (далее CRM 4.0 для краткости) может оказаться непростой задачей из-за асинхронной природы вызовов Silverlight и ее неспособности напрямую вызывать веб-сервисы CRM 4.0. В этой статье подробно поясняется, как создать приложение Silverlight, которое может читать и записывать данные через CRM 4.0 Web service API.
Обзор решения
Silverlight 4 и CRM 4.0 — мощные технологии, но их не так-то просто интегрировать. Я расскажу о том, что стоит за этой интеграцией, исследовав асинхронное взаимодействие между Silverlight и CRM 4.0 Web service API.
В типичных приложениях, не имеющих отношения к Silverlight, вызов веб-сервиса осуществляется синхронно: он вызывается, и приложение ждет, пока не будет получен ответ. В течение этого времени пользователь не может взаимодействовать с приложением и должен ждать окончания вызова. В асинхронном приложении, например на основе Silverlight, вызов сервиса выдается, но приложение остается полностью функциональным даже до получения ответа. Это значительно улучшает удобство использования приложения, но предъявляет более высокие требования к разработчику.
Чтобы понять, как осуществляется это взаимодействие, я представлю несколько примеров. Для начала мы посмотрим, как настроить приложение Silverlight, и пошагово пройдем все операции, необходимые для взаимодействия с веб-сервисом, который будет действовать как оболочка CRM 4.0 API. Затем мы изучим детали работы с CRM 4.0 API и обсудим, как читать и записывать данные с использованием оболочки — веб-сервиса. Мы поработаем с базовой сущностью System User в CRM 4.0, а также рассмотрим работу с динамическими сущностями. Наконец, вы узнаете, как обращаться с наборами результатов, возвращаемых в Silverlight. В итоге вы сможете легко интегрировать свои приложения Silverlight с CRM 4.0.
Создание приложения Silverlight 4
Чтобы сконфигурировать приложение Silverlight на взаимодействие с CRM 4.0, потребуется выполнить ряд операций. Хотя можно ссылаться на CRM 4.0 SDK или Web service API прямо из проекта Silverlight, большинство интерфейсов и методов на самом деле из кода вызываться не будет. Для взаимодействия с CRM 4.0 API нужно создать оболочку в виде веб-сервиса. Этот веб-сервис станет посредником при вызовах между приложением Silverlight и CRM 4.0 API в формате, который будет понятен Silverlight. Оболочку в виде веб-сервиса можно добавить прямо в приложение SilverlightCRMDemo.Web.
Начните с создания нового решения Silverlight в Visual Studio 2010 и назовите его CRM40SilverlightDemo. При создании приложения Silverlight в Visual Studio всегда генерируются два проекта. Один является базовым приложением Silverlight, а второй — приложением ASP.NET, встраивающим приложение Silverlight в веб-страницу. Это приложение ASP.NET также будет хостом для веб-сервиса, который будет взаимодействовать с CRM 4.0 API. Приложение Silverlight будет ссылаться на этот веб-сервис через Service Reference.
Чтобы создать оболочку в виде веб-сервиса, добавьте новый веб-сервис в приложение ASP.NET и назовите его CrmServiceWrapper. В этом примере нужно добавить в сервис два веб-метода: один — для получения CRM-данных, а второй — для их отправки. На рис. 1 показано, как на данном этапе должны выглядеть эти методы. Когда вы добьетесь успешного взаимодействия приложения Silverlight с этим сервисом-оболочкой, вы модифицируете эти методы для реального вызова CRM 4.0 API.
Рис. 1. Заглушки веб-методов
public class CrmServiceWrapper : System.Web.Services.WebService { [WebMethod] public string GetCRMData() { return "This is the stubbed return for retrieving"; } [WebMethod] public string PostCRMData() { return "This is the stubbed return for posting data"; } }
После включения веб-сервиса в приложение ASP.NET самый простой способ добавить ссылку на него из приложения Silverlight — запустить его в отладочном режиме и получить URL, с которого отладчик запускает приложение ASP.NET (вы можете скопировать URL из всплывающего окна браузера). Теперь вы можете добавить новую Service Reference с именем CrmServiceReference в приложение Silverlight и вставить этот URL. Все релевантные конфигурационные файлы и код будут автоматически скорректированы. Если вы решите не делать этого, вам придется иметь дело с исключениями ссылок между доменами и немного повозиться с настройкой, чтобы успешно отладить свое приложение.
Теперь, когда у вас есть ссылка, можно приступать к реальному кодированию в приложении Silverlight. Код должен подключать обработчик событий для каждого веб-метода и создавать два метода для обработки данных по завершении вызовов этих веб-методов. Код, показанный на рис. 2, можно напрямую вставить в файл MainPage.xaml.cs приложения Silverlight. Запуск этого файла приведет к одновременному выполнению обоих методов.
Рис. 2. Добавление кода в MainPage.xaml.cs
using CRM40SilverlightDemo.CrmServiceReference; public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); // Call the GetCRMData Web method. CrmServiceWrapperSoapClient proxyGet = new CrmServiceWrapperSoapClient(); proxyGet.GetCRMDataCompleted += new EventHandler<GetCRMDataCompletedEventArgs>(proxy_GetCRMDataCompleted); proxyGet.GetCRMDataAsync(); // Call the PostCRMData Web method. CrmServiceWrapperSoapClient proxyPost = new CrmServiceWrapperSoapClient(); proxyPost.PostCRMDataCompleted += new EventHandler<PostCRMDataCompletedEventArgs>(proxy_PostCRMDataCompleted); proxyPost.PostCRMDataAsync(); } // Called asynchronously when the GetCRMData Web method returns data. void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e) { // Do something with the data returned. string result = e.Result.ToString(); } // Called asynchronously when the PostCRMData Web method returns data. void proxy_PostCRMDataCompleted(object sender, PostCRMDataCompletedEventArgs e) { // Do something with the data returned. string result = e.Result.ToString(); } }
Убедившись, что ваше приложение Silverlight выполняется без ошибок и получает данные от веб-сервиса, вы можете переключиться на вызовы CRM 4.0 API. Все эти вызовы будет исходить из уже созданных веб-методов GetCRMData и PostCRMData оболочки веб-сервиса.
Взаимодействие с CRM 4.0 API
Через CRM 4.0 доступны два основных веб-сервиса: CRMService и MetadataService. На эти веб-сервисы, в целом, можно ссылаться из любого проекта (но они не дают особой функциональности, если вы ссылаетесь на них из приложения Silverlight). Самый распространенный и эффективный способ работы с этим API — использование Microsoft Dynamics CRM SDK (можно скачать по ссылке bit.ly/6M3PvV). SDK содержит множество классов и методов и упрощает взаимодействие между .NET-кодом и веб-сервисами CRM 4.0. В этом разделе вы научитесь взаимодействовать с API из оболочки веб-сервиса, используя SDK.
Первый шаг — добавление ссылок на соответствующие сборки CRM SDK. В приложении ASP.NET, где размещается оболочка в виде веб-сервиса, добавьте ссылки на две сборки SDK: microsoft.crm.sdk.dll и microsoft.crm.sdktypeproxy.dll. После этого включите необходимые директивы в начало страницы CrmServiceWrapper.asmx:
using Microsoft.Crm.Sdk; using Microsoft.Crm.SdkTypeProxy; using Microsoft.Crm.Sdk.Query;
Следующий шаг — напишите код для создания экземпляра CRM-сервиса, который позволит вам подключаться к экземпляру CRM 4.0. Этот код будет использоваться веб-методами GetCRMData и PostCRMData, поэтому его нужно выделить в собственный метод. Этот метод (рис. 3) требует двух ключевых полей: названия организации для вашего экземпляра CRM 4.0 и URL основного CRM-сервиса (находится по адресу /MSCRMServices/2007/crmservice.asmx). Заметьте, что эти поля лучше всего размещать в конфигурационном файле для упрощения их модификации после компиляции кода.
Рис. 3. Метод GetCRMService
static public CrmService GetCRMService() { CrmService service = new CrmService(); CrmAuthenticationToken token = new Microsoft.Crm.Sdk.CrmAuthenticationToken(); token.OrganizationName = "Contoso"; service.Url = "http://localhost:5555/MSCRMServices/2007/crmservice.asmx"; service.Credentials = System.Net.CredentialCache.DefaultCredentials; service.CrmAuthenticationTokenValue = token; return service; }
Запрос CRM-данных
Теперь вы можете переключить свое внимание на запрос данных от CRM 4.0. Важно знать, что в CRM 4.0 есть два типа сущностей: базовые системные и собственные. С базовыми системными сущностями работать немного легче, чем с собственными сущностями. По умолчанию все свойства базовых сущностей можно получать через строго типизированные объекты в C#. Собственные сущности, как правило, запрашиваются в виде Dynamic Entities, хотя в качестве альтернативы их тоже можно обрабатывать как строго типизированные объекты. Я продемонстрирую запрос данных от двух типов сущностей.
Пример запроса базовой системной сущности (System User) показан на рис. 4. Этот код заменяет веб-метод с тем же именем, который был в виде заглушки ранее в этой статье. В новом коде, который запрашивает от CRM 4.0 всех системных пользователей, вы заметите несколько важных вещей. Во-первых, тип объекта, с которым мы имеем дело, — systemuser. Все базовые сущности имеют свои типы. Во-вторых, возвращаемый результат является строковым представлением XML-документа.
Рис. 4. Запрос сущности System User
[WebMethod] public string GetCRMData() { // This will return all users in CRM in a single XML structure. StringBuilder xml = new StringBuilder(); CrmService service = GetCRMService(); QueryExpression query = new QueryExpression(); query.EntityName = "systemuser"; query.ColumnSet = new AllColumns(); RetrieveMultipleRequest retrieve = new RetrieveMultipleRequest(); retrieve.Query = query; retrieve.ReturnDynamicEntities = false; RetrieveMultipleResponse retrieved = (RetrieveMultipleResponse)service.Execute(retrieve); xml.Append("<Users>"); for (int i = 0; i < retrieved.BusinessEntityCollection.BusinessEntities.Count; i++) { systemuser user = (systemuser)retrieved.BusinessEntityCollection.BusinessEntities[i]; // Create a string represenation to return to Silverlight app. xml.Append("<User"); xml.Append(" FirstName ='" + user.firstname + "'"); xml.Append(" LastName = '" + user.lastname + "'"); xml.Append(" SystemUserId = '" + user.systemuserid.ToString() + "'"); xml.Append(" JobTitle = '" + user.jobtitle + "'"); xml.Append("/>"); } xml.Append("</Users>"); return xml.ToString(); }
Вы обнаружите, что варианты возврата данных в Silverlight весьма ограничены. Например, вы не можете получить BusinessEntityCollection, так как Silverlight не позволяет напрямую работать с этим API. Также есть ограничения на передачу XML в Silverlight через веб-сервис. Поэтому в итоге лучший вариант — простая строка.
Запрос собственных сущностей может оказаться посложнее. Наиболее распространенный способ получения данных — использование Dynamic Entity для извлечения результатов (пример такого рода показан на рис. 5). Проблема с этим подходом в том, как работать со специфическими атрибутами в фильтрах внутри выражений запроса. И хотя все это возможно, особой помощи от IntelliSense не ждите.
Рис. 5. Получение данных с использованием Dynamic Entity
public static DynamicEntity GetCRMEntity( CrmService tmpService, String entityId, String entityName) { DynamicEntity crmEntity = null; TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic(); // Set the properties of the target. targetRetrieve.EntityName = entityName; targetRetrieve.EntityId = new Guid(entityId); // Create the request object. RetrieveRequest retrieve = new RetrieveRequest(); // Set the properties of the request object. retrieve.Target = targetRetrieve; retrieve.ColumnSet = new AllColumns(); // Retrieve as a DynamicEntity. retrieve.ReturnDynamicEntities = true; // Execute the request. RetrieveResponse retrieved = (RetrieveResponse)tmpService.Execute(retrieve); // Extract the DynamicEntity from the request. DynamicEntity entity = (DynamicEntity)retrieved.BusinessEntity; crmEntity = entity; return crmEntity; }
Подход с Dynamic Entity, вероятно, самый распространенный, но, если вы хотите полностью видеть структуру своей сущности в Visual Studio и взаимодействовать с ней так же, как и со стандартной сущностью, вы можете создать прокси для CrmService. Во многих случаях это может дать более широкие возможности в разработке и обеспечить большую гибкость в том, как пишется код. Прокси — это не более чем C#-файл, сгенерированный на основе самого нового экземпляра CrmService WSDL. Чтобы создать класс прокси для основного сервиса CrmService, откройте командную строку Visual Studio и введите следующее, заменив URL ссылкой на свою страницу crmservice.asmx:
wsdl.exe /out:CrmSdk.cs /namespace:CRM40SilverlightDemo.WebReferences.CrmSdkhttp://localhost:5555/mscrmservices/2007/crmservice.asmx?wsdl
Эта команда создаст C#-файл CrmSdk.cs в каталоге, откуда вы запустили wsdl.exe. Этот файл нужно добавить в ваш проект. После этого вы можете работать с любой собственной сущностью точно так же, как с базовыми системными сущностями. Если сущность изменилась, просто обновите свой C#-файл прокси, и новые атрибуты (или другие модификации) станут доступными. В нашем текущем упражнении C#-файл прокси не используется.
Обновление данных в CRM 4.0
Разобравшись в том, как получать данные от CRM 4.0, вы сможете понять и то, как их передавать. Код на рис. 6 показывает, как обновить запись системного пользователя. Для этого нужно передать два свойства: уникальный идентификатор записи CRM 4.0 и свой ник. Чтобы отправить эти строки, в файле MainPage.xaml.cs нужно модифицировать одну строку кода:
proxyPost.PostCRMDataAsync("f04b02d9-ad5f-e011-a513-000c29330bd5","My Nickname");
Заметьте, что этот идентификатор «зашит» в вызов метода PostCRMData. На практике вы предпочтете создать механизм для динамического получения идентификаторов.
Рис. 6. Отправка данных в CRM 4.0
[WebMethod] public string PostCRMData(string userId, string nickname) { CrmService service = GetCRMService(); Key kid = new Key(); kid.Value = new Guid(userId); systemuser entity = new systemuser(); entity.systemuserid = kid; entity.nickname = nickname; service.Update(entity); return "success"; }
Обработка результатов в Silverlight
К этому моменту решение должно извлекать данные из CRM 4.0 и отправлять их. Однако мы пока ничего не делали со строковыми результатами, возвращаемыми в Silverlight. Метод GetCRMData вызовращает строку данных, содержащую XML-документ со всеми пользовательскими записями, но что делать с этими данными? В зависимости от конкретного элемента управления вы можете напрямую связать его с XML или предварительно разобрать возвращаемый XML и работать с индивидуальными элементами данных.
Пример разбора результатов в цикле показан на рис. 7. Этот код демонстрирует, как загрузить строку в XML-документ и перебрать данные. Для работы с XML-документами в Silverlight самая универсальная функциональность заключена в классе XDocument. Для его использования нужно добавить в свой проект Silverlight ссылку на System.Xml.Linq.
Рис. 7. Работа с XDocument
void proxy_GetCRMDataCompleted(object sender, GetCRMDataCompletedEventArgs e) { XDocument xDoc = XDocument.Parse(e.Result); string firstName; string lastName; string ID; string title; // Loop through the results. foreach (XElement element in xDoc.Descendants("User")) { firstName = GetAttributeValue(element, "FirstName"); lastName = GetAttributeValue(element, "LastName"); ID = GetAttributeValue(element, "SystemUserId"); title = GetAttributeValue(element, "JobTitle"); } } private string GetAttributeValue(XElement element, string strAttributeName) { if (element.Attribute(strAttributeName) != null) { return element.Attribute(strAttributeName).Value; } return string.Empty; }
Безграничные возможности
При интеграции эти двух технологий вы получаете безграничные возможности. Некоторые шаги, которые вы наверняка захотите предпринять, — разобраться в подходах к обработке исключений (многие элементы управления Silverlight скрывают исключения, поэтому в каждом случае потребуется индивидуальный подход) и интеграции с различными элементами управления. Независимо от выбранного вами направления дальнейшей работы у вас теперь есть все, что нужно для создания решений Silverlight, способных читать и записывать данные CRM 4.0.