Расширенная функциональность
13
Июл
4

CRM 2011 или найди 10 отличий… Разработка, эпизод II

Веб-службы


WCF

С новыми конечными точками CRM, полностью основанными на .Net 4 и WCF, можно легко и быстро взаимодействовать с CRM в прямом и обратном направлении. Новая Веб-служба WCF также обеспечивает существенное повышение производительности по сравнению с классическим ASMX Web Service.

URL веб-сервисов:

  • http://{server}/XRMServices/2011/Discovery.svc – Discovery Service;
  • http://{server}/{OrgName}/XRMServices/2011/Organization.svc – конечная точка SOAP для организации.

Пример использования сервиса для создания Контакта (через WCF сервис организации):

Entity myContact = new Entity(); 
myContact.LogicalName = "contact";

AttributeCollection myAttColl = new AttributeCollection();
myAttColl.Add(new KeyValuePair<string, object>("lastname", "Rana"));

myContact.Attributes = myAttColl;

ClientCredentials credentials = new ClientCredentials();
Uri organizationUri = new Uri("http://servername:port/organizationname/xrmServices/2011/organization.svc");

try
{
    OrganizationServiceClient orgClient = new OrganizationServiceClient(); 
    orgClient.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(“username”, “password”, “domain”); 
    orgClient.Create(myContact);
}
catch (Exception ex)
{
    throw ex;
}

OData (REST)

OData отрытый стандарт для выполнения операций чтения, создания и обновления с помощью параметров запроса в строке URL. OData использует стандартные HTTP сообщения (GET, PUT, POST и DELETE) и свой синтаксис. Чтобы использовать OData, Вы обращаетесь к конечной точке веб-службы. По существу это URL. Напрмер, www.foo.com/mydata.svc. Обращение по этому адресу возвратило бы данные в виде XML. В каждой конечной точки службы будет набор данных. Для доступа к конкретным даннм необходимо конкретезировать запрос с помощью параметров.

Также OData (REST) – новая конечная точка, добавленииая в CRM 2011, которая позволяет Вам работать с данными CRM без необходимости сложного кодирования. Эта конечная точка облегчает работу с операциями CRUD (Create Retrive Update Delete) через скрипты. Другие операции (за исключением Создания, Получения, Обновления и Удаления) не поддерживаются. Данные могут возвращаться в формате Atom или Json.

В Microsoft Dynamics CRM 2011 у каждой организации есть свой сервис OData и каждый объект представлен как «набор» данных. URL OData сервисов в CRM 2001 имеют следующий вид:

  • Главная точка доступа OData сервиса (возвращает список коллекций по умолчанию): http://crmserver/orgname/xrmservices/2011/organizationdata.svc
  • Метаданные: http://crmserver/orgname/xrmservices/2011/organizationdata.svc/$metadata

Команды OData могут легко быть проверены в IE. Для этого Вам необходимо выключить feed reading. Для этого откройте свойства IE – вкладка Content – в разделе Feeds and Web Slice щелкните Setting – в открывшемся окне снемите галку Turn on reading view. После этого можете попробовать ввести их в адресной строке браузера и увидите возвращенные XML-данные. А чтобы вытащить запись или записи какого-либо объекта Вы должны сначал узнать имя его набора. Дефолтный URL (http://crmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc) возвращает имена всех наборов, к которым Вы можете обратиться.


CRM 2011 поддерживает следующие операторы oData:

Оператор Описание
$expand Указывает, что связанные записи должны быть получены в возвращаемом наборе.
$filter Определяет выражение или функцию, которая должна быть равна «true» для записи, чтобы она была возвращена в наборе.
$orderby Определяет, каким образов должен быть упорядочен набор записей.
$select Определяет набор полей, которые должны быть возвращены.
$skip Определяет записи, которые должны быть пропущены в возвращаемом наборе.
$top Определяет максимальное количество возвращаемых записей.

Операторы, которые вы моежет использовать в параметре $filter:

eq =
ne <>
gt >
ge >=
lt <
le <=
and логическое объединение
or логическое или
not логическое отрицание

Внимаение: регистр имеет значение.

Примеры URL oData

Цель URL
Возвращение 50 записей Организаций http://<crmserver>/<orgname>/xrmservices/2011/organizationdata.svc
/AccountSet/
Выборка поля Name https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$select=Name

P.S. если поля не заданы, то возвращается все поля

Выборка двух полей: Name и StateCode https://gtblog.crm5.dynamics.com/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$select=Name,StateCode
Возвращаем какую-либо конкретную запись http://crmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/AccountSet(guid’3D37ACD3-5846-E011-8AE1-080027131692′)

При этом будут возвращаться все поля, вне зависимости от того, заполнены они или нет (если в параметре не укзано обратное).

Возвращаем только некоторые поля нужной записи http://crmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/AccountSet(guid'{3D37ACD3-5846-E011-8AE1-080027131692}’)?$select=AccountNumber,AccountName
Фильтрация по полю Name http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$filter=Name%20eq20’Script X’
Фильтрация по двум полям: Name,
StateCode
http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$filter=Name%20eq%20’Script X’%20and%20StateCode/Value%20eq%200
Выборка одного поля и фильтр http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$select=Name&filter=Name%20eq20’Script X’
Сортировка по полю Name http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$orderby=Name
Name,
StateCode
http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$orderby=Name,StateCode
Ограничение выборки http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$top=1&$orderby=Name
Возвращение ограниченного количества записе (в данном случае одной записи) http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$select=WorkflowId&$filter=Name%20eq%20’Script X’%20and%20StateCode/Value%20eq%200&$top=1
Возвращаем Организации, связанные со Звонками http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/AccountSet?$expand=Account_Phonecalls

З.Ы. в параметре expand передается название отношения.

Отбираем записи в кторых поле Name начинается с «а» http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$filter=startswith(Name, ‘a’)
Отбираем записи в кторых поле Name содержит слово «store» http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$filter=substringof(‘store’, Name)
Отбираем записи в кторых поле Name заканчивается на «е» http://gcrmdev:5555/CRMDEV/XRMServices/2011/OrganizationData.svc
/WorkflowSet?$filter=endswith(Name, ‘e’)

З.Ы. Важно учитывать регистр при наборе команд OData.

%20

Вы должны будете заменить в URL пробелы на символы %20, а JS-коде использовать функцию escape().

Например, так:

https://<crmserver>/<orgname>/XRMServices/2011/OrganizationData.svc/WorkflowSet?$filter=Name%20eq%20‘Inbound Call Script’

А в JS-коде так:

// Считываем контекст, чтобы получить CRM URL
var context = GetGlobalContext();
var serverUrl = context.getServerUrl();

// Определяем конечную точку ODATA и колекцию объекта
var ODATA_ENDPOINT = "XRMServices/2011/OrganizationData.svc";
var ODATA_EntityCollection = "/WorkflowSet";

// Определяем ODATA фильтр
var ODATA_Filter = "Name eq \'Inbound Call Script\'";

// Оюъеденяем все это в окончательный URL
var ODATA_Final_url = serverUrl + ODATA_ENDPOINT + ODATA_EntityCollection + "?$filter=" + escape(ODATA_Filter)

Некоторые из ограничений при использовании операторов:

Оператор Ограничение
$expand Максимальное количество связанных уровней — 6
$top Размер страницы ограничен максимум 50 записями
$skip При использовании с distinct-запросов, мы ограничены общим количеством (skip + top) в 5000 записей.
$select Разрешен выбор только полей одного уровеня.
$filter Условия разрешены только для одной группы атрибутов. Группой атрибута я обращаюсь к ряду условий, к которым присоединяются И/или пункт.

Группа атрибутов может быть на корневом объекте:

…/TaskSet?$expand=Contact_Tasks&$filter=Subject eq ‘test’ and Subject ne null

… или на расширенном объекте.

…/TaskSet?$expand=Contact_Tasks&$filter=Contact_Tasks/FirstName eq ‘123‘

Арифметические, математические и операторы даты/веремени не поддерживаются.

$orderby Сортировка разрешена только для корневого объекта.
Навигация Только один уровень навигации разрешен в любом направлении отношений. Отношения могут быть 1:N, N:1 или N:N

Поддерживаемые операции SOAP и REST:

Запрос Сервис
Create, Retrieve, Update, Delete REST, SOAP
Associate, disassociate REST, SOAP
Assign SOAP
Retrieve Metadata SOAP
Execute SOAP

З.Ы. Аутентификация в oData сообщениях возможна только в пределах веб-приложениях. Поэтому Вы можете использовать oData протокол только либо из JavaScript, либо из Веб-ресурсов Silverlight.

Пример использования в JavaScript

//Get Entity Attributes based on ID
var context;
var serverUrl;
var ODataPath;

function Getinfo() {
    context = Xrm.Page.context;
    serverUrl = context.getServerUrl();
    ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
}

function GetID() {
    var ID = Xrm.Page.getAttribute("Nameof ID field").getValue()[0].id;
    RetrieveAccountRecord(ID);
}

function RetrieveAccountRecord(Id) {
    var RetrieveAccountReq = new XMLHttpRequest();
    RetrieveAccountReq.open("GET", ODataPath + "/AccountSet(guid'" + Id + "')", true); //I am fetch account data
    RetrieveAccountReq.setRequestHeader("Accept", "application/json");
    RetrieveAccountReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
    RetrieveAccountReq.onreadystatechange = function() {
        RetrieveAccountReqCallBack(this);
    };
    RetrieveAccountReq.send();
}

function RetrieveAccountReqCallBack(RetrieveAccountReq) {
    if (RetrieveAccountReq.readyState == 4 /* complete */) {
        if (RetrieveAccountReq.status == 200) {
            var RetrievedAccount = this.parent.JSON.parse(RetrieveAccountReq.responseText).d;
            if(RetrievedAccount.Telephone1!=null)
                Xrm.Page.getAttribute("TargetTelephonefield").setValue(retrievedAccount.Telephone1);
            if(retrievedAccount.Emailaddress1!=null)
                Xrm.Page.getAttribute("TargetEmailfield").setValue(retrievedAccount.Emailaddress1);
        } else {
            errorHandler(retrieveAccountReq);
            alert("Error in Fetch Account data");
        }
    }
}

Плагины


Транзакции

В CRM 4.0 Вы могли зарегистрировать плагин, чтобы работать или Перед (pre-event) или После (post-event) операции платформы CRM. Однако Вы не могли запустить его непосредственно как часть транзакции. В CRM 2011 Вы теперь можете зарегистрировать Ваш плагин как часть работы платформы. Plug-in Registration Tool был соответственно изменен, чтобы поддерживать это нововведение. Если произойдет исключение, то будет выполнен откат транзакции. Вы можете зарегистрировать плагин для выполнения в транзакции на одну из двухстадий:

Событие Название стадии Номер стадии Описание
Pre-Event Pre-operation 20 Стадия в конвеере для плагинов, которые должны выполниться перед главной системной операцией. Плагины, зарегистрированные на этой стадии, выполняются в транзакции.
Post-Event Post-operation 40 Стадия в конвеере для плагинов, которые должны выполняться после главной операции. Плагины, зарегистрированные на этой стадии, выполняются в пределах транзации.


Parent\Child Pipeline

В CRM 4.0, существовал parent и child pipeline. В CRM 2011 эти конвейеры были консолидированы в один конвейер (т.е. их больше нету). Pre-event плагин, зарегистрированный в parent pipeline в CRM 4.0, эквивалентен регистрации на стадии 10. Pre-event плагин, зарегистрированный в child pipeline в CRM 4.0, эквивалентен регистрации на стадии 20.

Решения

Плагины и их шаги можно переносить через Решения (при этом в в конкретное Решение можно включить лишь часть зарегистрированных шагов какого-либо плагина)! Хотя регестрирровать и перерегестрировать их по-прежнему нужно через Plug-in Registration Tool.

Также учитите, что безопасная конфигурация шагов плагинов не экспортирются вместе с Решением.

Plugin Registration Tool

Plugin Registration Tool попрежнему используется для регистрации плагинов в CRM. Конечно он претирпел некоторые изменения, чтобы поддерживать новую функциональность, но в целом все осталось на своих местах, если не считать того что его больше нет в готовом виде (ну, или пока нет). Но есть его исходники, которые Вам потребуется скомпилировать, чтобы воспользоваться им.

Для этого нам понадобится:

  • .Net 4 Framework;
  • Visual Studio 2010;
  • Также нам понадобится Windows Identity Foundation, чтобы добавить ссылку на Microsoft.IdentityModel;
  • Ну, и конечно же CRM 2011 SDK, посколько именно в нем содержаться исходники Plugin Registration Tool;
  • Откройте в VS 2010 решение <CRM2011SDK>\sdk\tools\pluginregistration\;
  • Скомпилируйте решение;
  • Запустите Plugin Registration Tool из папки <CRM2011SDK>\sdk\tools\pluginregistration\bin\Debug.



Sandbox

CRM 2011 поддерживает выполнение плагинов в изолированной среде. В этой изолированной среде, также известной как sandbox (песочница), плагин может использовать возможности CRM SDK, чтобы получить доступ к веб-сервису организации. Доступ к файловой системе, системному журналу, определенным сетевым протоколам, реестру и некоторым другим функциям (reflection, LINQ и т.д.) из «песочнецы» запрещен.

Платформа CRM собирает мониторит плагины, которые выполняются в песочнице. Если рабочий процесс песочницы, который размещает плагин, превысит пороговые пределы CPU/памяти или будет иначе недоступен, то этот процесс будет уничтожен платформой. В тот же момент любые выполняющиеся плагины в том же рабочем процессе перестанут работать и вернут исключение. Однако, при следующем выполнении плагина он будет работать в обычном режиме. На одину организацию приходится один рабочий процесс, поэтому ошибки в одной организации не будут влиять на другую организацию.

Платформа CRM также собирает информацию о выполнении плагинов, которые выполняются в песочнице. Эта информация хранится в базы данных с помощью объекта PluginTypeStatistic. Эти записи от 30 до часа записываются в БД после того плагин выполнится.

Отладка плагинов

Следующие службы подключаются к Visual Studio и используются для отладки плагинов в CRM 2011:

Конфигурация плагина Service Process
online w3wp.exe
offline Microsoft.Crm.Application.Hoster.exe
Асинхронный плагин или кастомный Бизнес-процесс CrmAsyncService.exe
sandbox (isolation mode) Microsoft.Crm.Sandbox.WorkerProcess.exe

Синтаксис

Синтаксиса плагинов в CRM 2011 притерпел значительные изменения. Вот некоторых из них:

  • Microsoft.Crm.Sdk был заменен на Microsoft.Xrm.Sdk
  • Появились новые ссылки:
    • System.ServiceModel
    • System.Runtime.Serialization
  • Параметр по умолчанию для выполнения метода Execute был изменен с IPluginExecutionContext на IServiceProvide:
    public void Execute(IServiceProvider serviceProvider)
    
  • IpluginExecutionContext был изменен на сервисный тип и получает ссылку с помощью следующего синтаксис:
    IPluginExecutionContext context = (IPluginExecutionContext) serviceProvider.GetService(typeof(IPluginExecutionContext));
    
  • ICrmService был изменен на IOrganizationService. Используем контекст выполнения плагина, чтобы получить ограниченную ссылку на CRM Webservice:
    IOrganizationServiceFactory serviceFactory =
    IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
    
  • Dynamic entity был изменен на «Entity»:
    // Получение текущей записи из контекста
    if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
    {
        Entity entity = (Entity)context.InputParameters["Target"];
    }
    
  • Свойства Dynamic Entity были заменены на коллекции атрибутов:
    entity.Attributes.Contains("accountnumber")
    
  • Получение Pre-снимака (с именем «PreImage») плагина (пост-снимок делается по аналогии):
    if (context.PreEntityImages.Contains(“PreImage”) && context.PreEntityImages["PreImage"] is Entity)
    {
        preMessageImage = (Entity)context.PreEntityImages["PreImage"];
        accountnumber = (String)preMessageImage.Attributes["accountnumber"];
    }
    
  • Строго типизированные классы (account, contact) отсутствуют в сборке Microsoft.XRM.Sdk, но есть исключения;
  • CRM 2011 предоставляет утилиту «crmsvcutil» дл генерации строго типизированных классов, которые могут использоваться в плагинах;
  • Строго типизированные классы были изменены, чтобы использовать их как нетипизированные классы:
    Task task = new Task();
    Task["description"] = "Test description"
    
  • Исключений SOAP больше нет:
    catch (FaultException ex)
    
  • В коде CRM 2011 появилось понятие свойства EntityReference, вместо существовавшего в CRM 4.0 типов Lookup, Owner и Customer. Следующая таблица показывает чтение поля лукап в CRM 2011 (и сравнивает с CRM 4.0) из снимка плагина:
    CRM 4 CRM 2011
    Microsoft.Crm.Sdk.Key opportunityid = (Microsoft.Crm.Sdk.Key) postImage.Properties["opportunityid"];
    Guid opportunityRecordId = opportunityid.Value;
    
    EntityReference opportunityid = (EntityReference) postImage.Attributes["opportunityid"];
    Guid opportunityRecordId = opportunityid.Id;
    
    Microsoft.Crm.Sdk.Customer customerid = (Microsoft.Crm.Sdk.Customer)postImage.Properties["customerid"];
    string customerRecordType = customerid.type;
    Guid customerRecordId = customerid.Value;
    
    EntityReference customerid = (EntityReference) postImage.Attributes["customerid"];
    string customerRecordType = customerid.Type;
    Guid customerRecordId = customerid.Id;
    

Пример шаблона простого плагина:

  • Создайте проект class library в VS 2010;
  • Подпишите сборку строгим ключем;
  • Измените Project Target Framework на 4.0;
  • Добавьте в проект следующие библиотеки:
    • Microsoft.Xrm.Sdk.dll
    • System.ServiceModel
    • System.Runtime.Serialization.
  • Замените код сборки на следующий:
    using System;
    using System.ServiceModel;
    using System.Runtime.Serialization;
    using Microsoft.Xrm.Sdk;
    
    namespace MyPlugin
    {
        public class MyPlugin : IPlugin
        {
            public void Execute(IServiceProvider serviceProvider)
            {
                // Получаем контекст плагина
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
    
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    // Получаем целевой объект
                    Entity entity = (Entity)context.InputParameters["Target"];
                    
                    // Проверяем тип объекта
                    if (entity.LogicalName != "entity logical name”)
                    {
                        return;
                    }
                    
                    // Проверяем, что во входящих параметрах плагина есть нужное поле
                    if (entity.Attributes.Contains("field name") != false)
                    {
                        return;
                    }
                    
                    try
                    {
                        // Получаем сервис через контекст плагина
                        IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                        IOrganizationService service = factory.CreateOrganizationService(context.UserId);
                        
                        //Plugin Code
                    }
                    catch (FaultException ex)
                    { 
                        throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
                    }
                }
            }
        }
    }
    

Ну, а дальше осталось написать разумный код, собрать сборку и зарегистрировать ее в CRM 🙂

Трассировка плагинов

SDK CRM предоставляет Вам способ включить детали для отладки в исключение плагина. Для этого Вы должны будете инициализировать ITracingService. Веб-сервис предоставляет детали трассировки и выводит их на экран всякий раз, когда плагин выдает исключение.

// Инициализируем службу
ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

После этого, при сбое в плагине Вы получите очень информационное сообщение.

tracingService.Trace(yourMessage);


Удаление асинхронных операций

С CRM 4.0 была проблема связанная с AsyncOperationBase. А точнее с ее быстрым ростом и как следствие замедлением раблоты всей CRM системы.

К счастью, в CRM 2011 есть встроенный функционал по удалению отработавших асинхронных операций. Как это делается с Бизнес-процессами, мы уже проходили. А в плагинами эта настройка (в виде галки) находится на форме регистрации шага плагина. Работает она только для асинхронных плагинов!


Кастомные Бизнес-процессы

Встречаем нововведения Бизнес-процессов…

  • Подключаемые сборки:
    using System.Activities;
    using Microsoft.Crm.Sdk.Messages;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;
    
  • Инициализаци параметров для Activity (на примере лукапа, обязательного для заполнения):
    [Input("Enter Case")]
    [ReferenceTarget("incident")]
    [RequiredArgument]
    [Default("3B036E3E-94F9-DE11-B508-00155DBA2902", " incident ")]
    public InArgument<EntityReference> CaseID { get; set; }
    

    Доуступ к ним осуществляется так:

    Guid caseid = CaseID.Get<EntityReference>(executionContext).Id;
    
  • Выходные параметры:
    [Output("outputSum")]
    [Default("23.3")]
    public OutArgument Sum { get; set; }
    
  • Определение класса: базовый клас сменился с SequenceActivity на CodeActivity:
    public class AddToMarketingList : CodeActivity
    
  • Метод Execute:
    protected override void Execute(CodeActivityContext executionContext)
    
  • Создание CRM сервиса:
    IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
    IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
    
    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
    
  • Доступ к значениям входных входных параметров:
    Guid ListId = MarketingListEntityReference.Get<EntityReference>(executionContext).Id;
    

Регистрация кастомого Бизнес-процесса

Процесс регистрации примерно такой же как и в CRM 4.0, но есть одно важное отличие. Во время регистрации кастомного Бизнес-процесса Вы определяете, отображаемое в пользовательском интерфейсе CRM называние шага Бизнес-процесса и название группы в которую он будет входить (см. скрин)!

З.Ы. FriendlyName и Name, которые Вы задаете при регистрации, должны быть одинаковыми!


ASP.NET

CRM 2011 вводит существенные ограничения на размещение ASP.Net расширений в пределах сайта CRM. По существу единственный способом сделать это, является использование кончных точек CRM 4.0. Если Вы будете использовать конечные точки CRM 2011, то Вам потребуется размещать страницы на отдельных веб-сайтах. И как следствие Вы должны будете использовать абсолютные URL при создании ссылке на Вашу страничку. А для единственного залогиневания в системе Вам придется использовать Secure Token Services. Вообщем перехоите на сервелат 🙂

З.Ы. более подробно по теме смотрите:

Код

System Types

CRM 2011 позволяет работать с типами .Net Framework вместо того, чтобы использовать CRM-специфичные типы (вроде CrmNumber). Следующая таблица показывает сопоставление между типом атрибутов старой и новой версией CRM:

Attribute Type Microsoft Dynamics CRM 2011 Type Microsoft DynamicsCRM4.0 Type
Boolean bool or System.Boolean CrmBoolean
CalendarRules EntityCollection DynamicEntity[] or calendarrule[]
Customer EntityReference Customer
DateTime System.DateTime CrmDateTime
Decimal decimal or System.Decimal CrmDecimal
Double double or System.Double CrmFloat
Integer int or System.Integer CrmNumber
Internal System.Object
Not used in records.
Not used in records.
Lookup EntityReference Lookup
Memo string or System.String System.String
Money Money CrmMoney
Owner EntityReference Owner
PartyList EntityCollection or ActivityParty[] activityparty[] or DynamicEntity []
Picklist OptionSetValue Picklist
PrimaryKey System.Guid Key
String System.String System.String
State OptionSetValue or enumeration generated for the entity state EntityNameStateInfo
Status OptionSetValue orint Status
Uniqueidentifier System.Guid UniqueIdentifier
Virtual System.Object
Not used in records.
Not used in records.

Классы

DynamicEntity

DynamicEntity теперь «называется» Entity (расположен в Microsoft.Xrm.Sdk). И работать с этим Entity гораздо проще и удобней чем с DynamicEntity. Например, так:

Entity entity = new Entity(Account.EntityLogicalName);
entity["accountid"] = Guid.NewGuid();

LINQ for CRM

Провайдер Linq for CRM, интегрированный в CRM 2011, сделает нашу жизнь намного проще при выполнении запросов к Dynamics CRM. За полной документацией конечно лучше обращаться в SDK, но чтобы оценить всю мощь LINQ, вот Вам небольшой пример, динамического запроса, который возвращает список Организаций из CRM 2011:

OrganizationServiceContext context = new OrganizationServiceContext(service);
var accounts = (from a in context.CreateQuery("account")
                where ((string)a["name"]).StartsWith("Microsoft")
                select new
                {
                    Id = (Guid)a["accountid"],
                    Name = (string)a["name"]
                }).ToList();

Трассировка ошибок SDK

При получении исключения кастомным кодом в CRM (напимер так: exception-handling), Вы можете увидеть следующий текст:

Timestamp: 4/3/2011 10:20:23 PM
Code: -2147220970
Message: System.Runtime.InteropServices.COMException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #7A49F455
Trace: 
Inner Fault: Has Inner Fault

Особого внимая здесь заслуживает код: #7A49F455. Этот код – «ссылка», по которой Вы можете легко найти соответствующее данной ошибке сообщение в трассировке CRM 🙂 Оно будет выглядеть примерно так:

[2011-04-03 17:20:23.307] Process: w3wp |Organization:00000000-0000-0000-0000-000000000000 |Thread: 25 |Category: Platform |User: 00000000-0000-0000-0000-000000000000 |Level: Error | ExceptionConverter.ConvertMessageAndErrorCode 
>System.Runtime.InteropServices.COMException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: <strong>#7A49F455</strong>: System.Runtime.InteropServices.COMException (0x8007054B): The specified domain either does not exist or could not be contacted.

Crmsvcutil

Если Вы любите раннее связывание, то в CRM 2011 Вас ждет небольшое разочарование – в нем отсутсвуеют строго типизированные классы, вроде Account и Contact. Но Вы можете воссоздать их с помощью утилиты crmsvcutil, которая на выходе создаст *.cs файл, содержащий набор объектов/классов для объектов организации CRM. Далее этойт файл необходимо подключить к Вашим библиотекам, чтобы пользоваться строго типизированными классами CRM.

CrmSvcUtil Вы сможете найти в папке <sdk>\bin. Работает он из коммандной строки примерно так:

CrmSvcUtil.exe /url:https://myport:555/MyOrg/xrmServices/2011/Organization.svc /out:GeneratedCode.cs /username:administrator /password:password

После чего Вам нужно в Visual Studio щелкнуть правой кнопкой мыши на проекте — Add — Existing Item и выбрать сгенерированный *.cs файл. После чего, можете использовать в своем проекте сгенерированные классы.

Следующая таблица приводит параметры, которые принимает утилита CrmSvcUtil:

Параметр Сокращение Описание Обязательный
deviceid di Device ID, используемый для аутентификации при подключении к online серверу CRM. Нет
devicepassword dp Device пароль, используемый для аутентификации при подключении к online серверу CRM. Нет
domain d Имя домена. Нет
url URL веб-сервиса организации. True
out o Имя выходного файла. True
language l Язык генерируемого файла. Может быть либо «CS», либо «VB». По умолчанию «CS». Нет
namespace n Namespace генерируемого кода. По умолчанию namespace «global». Нет
username u Имя пользователя. Нет
password p Пароль пользователя. Нет
servicecontextname The name of the generated organization service context class. If no value is supplied, no service context is created. Нет
help ? Show usage information. Нет
nologo Suppress the banner at runtime. Нет

Подключение к Visual Studio

В VS в меню Tools есть опция External Tools,с помошью которой Вы можете быстро вызвать exe-файл и передать ему какие-либо параметры. crmsvcutil идеальный кандидат для этой функции 🙂

  • Перейдите Tools — External Tools — щелкните Add;
  • Задайте каое-нибудь пользовательское название;
  • В command Вам необходимо определить путь с crmsvcutil. Например так:
    C:\Hosk Stuff\crm\CRM2011\sdk\bin\crmsvcutil.exe;
  • В arguements Вы задаете агрументы которые хотите передать crmsvcutil.exe. Например так:
    /url:http://crmserver:5555/crmorganisationname/xrmServices/2011/Organization.svc /out:earlyboundclasses.cs /username:hosk /password:Hoskspassword
    
  • Наконец в initial directory Вы определяете папку, куда будет выгружен выходной файл.



FetchXML

FetchXML в CRM 2011 включает группировку и агрегирование, которые позволяют Вам спользовать такие функции как sum, count и др.

Groupby

Испольуя Group мы можем вычислить сумму, среднее значение, минимум, максимум, количество.

Чтобы создать агрегирующий атрибут, Вы должны установить ключевое слово aggregate в true, а затем определить имя объекта, имя атрибута и имя переменной. Вы также должены определить тип агрегирования, которое хотите получить:

<fetch distinct='false' mapping='logical' aggregate='true'>
    <entity name='entity name'>
        <attribute name='attribute name' aggregate='count' alias='alias name'/>
    </entity>
</fetch>

Вы можете определить только один агрегирующий атрибут в запросе и при этом не можете использовать ключевое слово distinct.
Пример ниже показывает, как получить сумму полей totalamount всех выигранных Предложений:

string quotes = @"<fetch distinct='false' mapping='logical' aggregate='true'> " +
    "<entity name='quote'> " +
        "<attribute name='totalamount' alias='totalamount_sum' aggregate='sum'/> " +
        "<attribute name='statecode' groupby='true' alias='state' /> " +
        "<filter type='and'> " +
            "<condition attribute=' statecode ' operator='eq' value='won' /> " +
        "</filter> " +
    "</entity> " +
"</fetch>";

EntityCollection quotes_result = _service.RetrieveMultiple(new FetchExpression(quotes));
foreach (var q in quotes_result.Entities)
{
    Decimal wonQuoteAmount = ((Money)((AliasedValue)q["totalamount_sum"]).Value).Value;
}

З.Ы. groupby не поддерживается в LINQ in CRM.

Количество записей

FetchXML в CRM 2011 также обзавелся возможностью возвращать колчествто записей возвращаемых запросом (это можно было делать и в CRM 4.0, но немного сложнее). Для этого достаточно воспользоваться трибутом ReturnTot alRecordCount:

<fetch mapping='logical' count='25' returntotalrecordcount='true'>
    <entity name='account'>
        <attribute name='accountid' />
        <attribute name='accountnumber' />
        <attribute name='address1_city' />
        <attribute name='address1_telephone1' />
        <attribute name='name' />
        <order attribute='name' />
        <filter>
            <condition attribute='address1_city' operator='like' value='Greenville%' />
        </filter>
    </entity>
</fetch>

Visual Studio 2010

Теперь инструментом разработки для MS CRM 2011 является Visual Studio 2010.

Обучение

Комментарии (4)
  • Roman 13.07.2011

    Спасибо за столь детальное изложение !

  • Александр 13.07.2011

    > Особого внимая здесь заслуживает код: #7A49F455. Этот код – «ссылка», по которой Вы можете легко найти соответствующее данной ошибке сообщение в трассировке CRM 🙂 Оно будет выглядеть примерно так:

    а где эту трассировку искать?

  • slivka_83 13.07.2011

    Ее сначала нужно включить: http://support.microsoft.com/kb/907490/en-us

  • Mad Fox 13.07.2011

    Информация касательно oData протокола, в целом, очень полезна. Только просьба подправить пример «поиск по полю, которое содержит подстроку» — функция называется не substring, а substringof (пару часов убил, пытаясь выяснить, что не так).

*

code