Fetch-запросы из плагинов… и 5000 записей
Простой Fetch-запрос
Саымый простой пример Fetch-запроса на языке C# выглядит так:
- Создайте плагин, подключите SDK сборки и System.Web.Services;
- Повесьте на него такой код:
using System; using System.Collections.Generic; using System.Text; using Microsoft.Crm.Sdk; using Microsoft.Crm.SdkTypeProxy; using System.Xml; public class FetchXML : IPlugin { // При выполнении плагина... public void Execute(IPluginExecutionContext context) { // Настраиваем CRM Service через контекст плагина ICrmService crmService = context.CreateCrmService(true); // Задаем строку с Fetch-запросом string fetchXml = "<fetch mapping='logical'><entity name='account'><attribute name='accountid'/></entity></fetch>"; // Выполняем Fetch-запрос string result = crmService.Fetch(fetchXml); // Создаем новый XML документ и подсовываем ему результат Fetch-запроса XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(result); // Отбираем из XML документа узлы result и просматриваем их все XmlNodeList xmlNodes = xmlDoc.GetElementsByTagName("result"); foreach (XmlNode item in xmlNodes) { // Проверяем что accountid заполнен if (item["accountid"] != null) { // Выводим в сообщении accountid throw new Exception(item["accountid"].InnerText); } } } }
- Зарегистрируйте на любое событие и вызовите его в CRM.
P.S. Составить Fetch-запрос Вы сможите с помощью Расширенного поиска, FetchXML Wizard’а и FetchXML Builder’а.
Ну, и как альтернативу Fetch-запросу приведу равнозначный (по результату) пример запроса с помощью QueryExpression:
using System; using System.Collections.Generic; using System.Text; using Microsoft.Crm.Sdk; using Microsoft.Crm.SdkTypeProxy; using Microsoft.Crm.Sdk.Query; public class QueryEx : IPlugin { // При выполнении плагина... public void Execute(IPluginExecutionContext context) { // Настраиваем CRM Service через контекст плагина ICrmService crmService = context.CreateCrmService(true); // Составляем запрос, чтобы вытащить accountid всех бизнес-партнеров QueryExpression query = new QueryExpression(); query.EntityName = "account"; query.ColumnSet = new ColumnSet(new String[] { "accountid" }); // Формируем объект запроса RetrieveMultipleRequest req = new RetrieveMultipleRequest(); req.Query = query; // Отправляем запрос в CRM Service RetrieveMultipleResponse response = (RetrieveMultipleResponse)crmService.Execute(req); // Просматриваем каждую возвращенную запись и выводим в сообщении ее accountid foreach (account acct in response.BusinessEntityCollection.BusinessEntities) { throw new Exception(acct.accountid.Value.ToString()); } } }
5000 записей
У выше приведенного кода есть один существенный недостаток! CRM сервер на Fetch-запрос возвращает максимум 5000 записей за один раз!
Тут есть два решения:
- Вы должны добавить DWORD-ключ TurnOffFetchThrottling в реестр в ветке HKLM\Software\Microsoft\MSCRM. Если установить значение этого ключа в 1 (и перезапустить IIS), то у Вас не будет лимита в 5000 записей. Но соответственно, это скажется на производительности, так что применять этот метод следует с осторожностью;
- Изменить код Fetch-запроса так, чтобы он «постранично» (при том, что на одну «страницу» приходится максимум 5000 записей) обработал все записи и вернул результат полной выборки.
Расмотрим как реализовать второй вариант…
- Создайте плагин и повесьте на него такой код:
using System; using System.Collections.Generic; using System.Text; using Microsoft.Crm.Sdk; using Microsoft.Crm.SdkTypeProxy; using System.Xml; using System.Data; public class FetchXML : IPlugin { // Создаем переменную сервиса ICrmService crmService; // При выполнении плагина... public void Execute(IPluginExecutionContext context) { // Настраиваем CRM Service через контекст плагина crmService = context.CreateCrmService(true); // Задаем строку с Fetch-запросом string FetchXml = "<fetch mapping='logical'><entity name='account'><attribute name='accountid'/></entity></fetch>"; // Вызываем функцию fetchAll и передаем ей строку Fetch-запроса // Возвращенный результат помещаем в XML объект XmlDocument FetchXmlResult = new XmlDocument(); // Загружаем строку Fetch-запроса в XML документ FetchXmlResult.LoadXml(fetchAll(FetchXml)); // Просматриваем XML документ и выводим в сообщении значение поля accountid foreach (XmlNode node in FetchXmlResult.DocumentElement.ChildNodes) { string attributevalue = node.SelectSingleNode("accountid").InnerText; throw new Exception(attributevalue); } } // Функция возвращает все записи Fetch-запроса private string fetchAll(string sFetchXml) { // Переменная опеределяющая необходимость продолжать цикл bool bComplete = false; // Начинаем просмотр с первой страницы int iPage = 1; // Общий массив, в коториый будем помещать результаты временный выборок XmlDocument oResults = new XmlDocument(); // Повторяем постраничную выборку пока bComplete равен false while (!bComplete) { // XML документ для "разбора" строки Fetch-запроса XmlDocument oFetchXml = new XmlDocument(); // Загружаем строку Fetch-запроса в XML документ oFetchXml.LoadXml(sFetchXml); // Находим в XML документе узел fetch XmlNode oFetchNode = oFetchXml.SelectSingleNode("/fetch"); // Если в узле fetch нет атрибута, то создаем его // Но в любом случаи помещаем в этот атрибут номер текуще страницы (итерации) if (oFetchNode.Attributes["page"] == null) { XmlAttribute oPageAttribute = oFetchXml.CreateAttribute("page"); oPageAttribute.Value = iPage.ToString(); oFetchNode.Attributes.Append(oPageAttribute); } else { oFetchNode.Attributes["page"].Value = iPage.ToString(); } // Выполняем Fetch-запрос string sResultsXml = crmService.Fetch(oFetchXml.InnerXml); // Создаем временный массив, в который мы помещаем до 5000 записей за одну итерацию XmlDocument oTempXml = new XmlDocument(); oTempXml.LoadXml(sResultsXml); // Затем мы добавляем результаты временной выборки в собирательный XML объект oResults if (iPage == 1) { oResults.LoadXml(oTempXml.InnerXml); } else { XmlNodeList oNodes = oTempXml.SelectNodes("/resultset/result"); XmlNode oResultsetNode = oResults.SelectSingleNode("/resultset"); foreach (XmlNode oNode in oNodes) { XmlNode oNewNode = oResults.ImportNode(oNode, true); oResultsetNode.AppendChild(oNewNode); } } // Теперь мы должны выяснить, есть ли еще страницы для выборки // Для этого мы проверяем атрибут morerecords узла resultset // Если таке страницы есть, мы только инкриминипуем iPage, иначе устанавливаем bComplete в true, чем прерываем цикл XmlNode oMore = oTempXml.SelectSingleNode("/resultset"); if (oMore.Attributes["morerecords"].Value == "0") { bComplete = true; } else { iPage++; } } // Возвращаем содержимое собирательного массива oResults return oResults.InnerXml; } }
Этот код делает следующие:
- При вызове плгаина объявляется строка Fetch-запроса и происходит вызов функции fetchAll с передачей ей этой строки в качестве параметра;
- fetchAll «постранично» (максимум по 5000 записей на страницу) выполняет Fetch-запрос и скаладывает результат каждого промежуточного Fetch-запроса в собирательную переменную;
- Когда все «страницы» будут просмотрены общий результат будет возвращен в контекст выполнения плагина, где с ним можно провести дальнейшую обработку.
- Зарегистрируйте плагин в CRM и вызовите событие, на которое он был зарегистрирован.
Добрый день!
подскажите как можно выполнить фетч запрос из жава скрипта. к примеру я хочу из формы заказ выбрать в скрипте все продукты, посчитать их количество и сохранить количество в поле количество продуктов заказа.
Спасибо
Очень просто 🙂 http://msdn.microsoft.com/en-us/library/cc677073.aspx
просто волшебно!
большое спасибо!
можно еще вопросик:
у меня на форме есть поле со статусом «только для чтения». значение поля формируется автоматически (при помощи запроса фетч), однако после сохранения формы это поле остается пустым. я так понимаю что стандарт механизм сохранения не сохраняет поля для чтения. Каким образом я могу самостоятельно сохранить значение этого поля?
С Уважением,
Вам нужно выполнить такой код
чтобы отправить на сервер новое значение поля, которое с посощью настроек переведено в состояние «только для чтения»