Разработка
05
Мар
2

Пример разработки кастомного бизнес-процесса

Разработка кастомных бизнес-процессов дает возможность нам объединить требования к системе с возможностями бизнес-процессов Microsoft CRM 4.0. Основными этими возможностями являются: настраиваемость через визуальный пользовательский интерфейс, многократность использования и т.д. Что делает реализацию той или иной задачи через бизнес-процессы весьма привлекательной.

Для примера разработки кастомного бизнес-процеса, рассмотрим такую часто встречающуюся заезжанную 🙂 задачу, как автонумирация записей. Как это будет выглядеть: поскольку каждая запись CRM уже сопровождается уникальным идентификатором, то велосипед изобретать не нужно – достаточно получить этот идентификатор (GUID). А чтобы как-то «отделить» его от GUID’а, добавим ему префикс «БП-» (сокращение от Бизнес-партнер — именно для него будем разрабатывать бизнес-процесс). Т.е. в конечном итоге наш номер будет иметь такой вид: «БП-» + GUID.

Настройка CRM

Прежде всего необходимо внести изменения в CRM – создайте в объекте Бизнес-партнер новой поле для хранения нашего номера:

  • Имя: autonumber;
  • Тип: текстовое.

Вынесите его на форму и сделайте доступным только для чтения.


Разработка бизнес-процесса

Для разработки кастомных бизнес-процессов используются следующие компоненты и инструменты:

Приступим…

  • Откройте Visual Studio (я буду показывать на 2008) и перейдите File — New — New Project. Выберите новый C#-проект с типом Workflow и шаблоном Workflow Activity Library;
  • Откроется визуальный редактор Workflow – он нам не нужен – закройте его. Щелкните правой кнопкой мыши по файлу *.cs в Solution Explorer и выберите Code — таким образом Вы переключитесь на представление сгенерированного кода. Из этого кода видно, что сгенерированный класс работает в контексте SequenceActivity, которая в свою очередь основана на библиотеке Windows Workflow Foundation. Поэтому опеределение кастомного бизнес-процесса должно находится внутри этого класса.
    Чтобы шаг кастомного бизнес-процесса был доступен через пользовательский Microsoft CRM 4.0, Вы должны объявить класс с атрибутом CrmWorkflowActivity и передать в качестве его параметров Имя шага и Имя группы, в которую он входит. Затем необходимо переопределить метод Execute, который вызывается во время выполнения бизнес-процесса, и выполнить какие-либо необходимые Вам действия. При этом метроду Execute передается ActivityExecutionContext, чтобы получить доступ к контексту бизнес-процесса, который содержит информацию о данном (выполняемом) экземпляре бизнес-процесса. Из контекста Вы можете получить информацию о primary entity (название, id, pre- и post-снимки) задействованного в бизнес-процесса, метод CreateCrmService, который возвращает ссылку на CrmService и т.д.
    В основном, это все, что Вы должны сделать, чтобы внедрить кастомный бизнес-процесс в Microsoft CRM 4.0;
  • Добавьте ссылки на Microsoft.Crm.Sdk.dll и Microsoft.Crm.SdkTypeProxy.dll (найти их Вы сможете в папке \Bin, с установиленным Microsoft Dynamics CRM 4.0 SDK);



  • Замените существующий код на такой:
    using System;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Drawing;
    using System.Workflow.ComponentModel.Compiler;
    using System.Workflow.ComponentModel.Serialization;
    using System.Workflow.ComponentModel;
    using System.Workflow.ComponentModel.Design;
    using System.Workflow.Runtime;
    using System.Workflow.Activities;
    using System.Workflow.Activities.Rules;
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.Workflow;
    using Microsoft.Crm.Workflow.Activities;
    using Microsoft.Crm.Sdk.Query;
    
    namespace Number
    {
        // Задаем имя (отображаемое на форме) кастомного шага бизнес-процесса, и имя группы в которую он входит
        [CrmWorkflowActivity("Автонумерация", "Мои бизнес-процессы")]
    
        // Класс кастомного workflow происходит из SequenceActivity
        public partial class Activity1 : SequenceActivity
        {
            // Создаем кастомные поля на форме кастомного шага бизнес процесса
    
            /*
               Свойство DependencyProperty используется, чтобы создать входной параметр.
               Оно объявляется с атрибутом CrmInput, чтобы получить доступ к параметру в пределах формы бизнес-процесса CRM.
               Параметр CrmReferenceTarget указывает на объект, который можно подставлять в это поле.
            */
    
            // Задаем имя поля, его тип и имя класса к которому он относится
            public static DependencyProperty objectIDProperty = DependencyProperty.Register("objectID", typeof(Lookup), typeof(Activity1));
    
            [CrmInput("objectID")]
            [CrmReferenceTarget("account")]
    
            public Lookup objectID
            {
                get
                {
                    return (Lookup)base.GetValue(objectIDProperty);
                }
                set
                {
                    base.SetValue(objectIDProperty, value);
                }
            }
    
            // Метод Execute вызывается бизнес-процессом во время работы, чтобы выполнить шаг
            // executionContext - контекст шага 
            protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                // Получаем контект сервиса
                IContextService contextService = (IContextService)executionContext.GetService(typeof(IContextService));
                IWorkflowContext context = contextService.Context;
    
                // Используем контекст сервиса, чтобы создать экземпляр класса CrmService
                ICrmService crmService = context.CreateCrmService();
    
                // Получаем GUID записи, находящейся в кастомном поле, созданного на форме шага бизнес-процесса
                Guid guidID = new Guid(this.objectID.Value.ToString());
    
                // Добавляем к GUID'у префикс
                string Number = "БП-" + guidID;
    
                // Создаем запрос, чтобы вернуть запись (в виде DynamicEntity) для которой выполнятеся бизнес-процесс
                TargetRetrieveAccount target = new TargetRetrieveAccount();
                target.EntityId = guidID;
    
                RetrieveRequest getAccount = new RetrieveRequest();
                getAccount.ReturnDynamicEntities = true;
                getAccount.Target = target;
                getAccount.ColumnSet = new AllColumns();
    
                RetrieveResponse retrieved = (RetrieveResponse)crmService.Execute(getAccount);
                DynamicEntity regardingAccount = (DynamicEntity)retrieved.BusinessEntity;
    
                // Если у возвращенной записи (объекта DynamicEntity) есть поле new_autonumber, то обновляем его, иначе добавляем к DynamicEntity новое поле и задаем для него значение
                if (regardingAccount.Properties.Contains("new_autonumber"))
                {
                    regardingAccount.Properties["new_autonumber"] = Number;
                }
                else
                {
                    regardingAccount.Properties.Add(new StringProperty("new_autonumber", Number));
                }
    
                // Обновляем запись, в отношении которой работает бизнес-процесс
                crmService.Update(regardingAccount);
    
                return ActivityExecutionStatus.Closed;
            }
        }
    }
    

    В этом коде происходит следующее:

    • Сначала задается имя нового кастомного шага и имя группы (шагов) в которую он будет входить;
    • Следующим этапом мы создаем новое поле, которое будет отображаться в настройках шага в CRM. Задаем тип этого поля – lookup. Определяем что в этот лукап можно подставить только Бизнес-партнера;
    • Получаем контекст выполнения бизнес-процесс, из которого создаем экземпляр класса CrmService;
    • Из лукапа бизнес-партнера заданного в кастомном шаге (и который подставится во время выполнения бизнес-процесс) вытаскиваем GUID (а т.к. мы подставим в этот лукап текущую запись, то это GUID текущей записи; этот же GUID можно вытащить через контекст бизнес-процесса, но об этом как-нибудь в другой раз 🙂 );
    • Формируем конечный вид номера: «БП-» + GUID;
    • Вытаскиваем из CRM текущую запись Бизнес-партнера (также по GUID’у) и обновляем в ней поле new_autonumber.

    При написании кода кастомного бизнес-процесса не используйте одно и то же имя для класса и для пространства имен – это может вызвать проблемы при публикации такого workflow в CRM.

  • Перейдите в Solution Explorer к файлу *.Designer.cs и убедитесь что имя класса и пространства имен в точносьт совпадают с теми, что Вы объявили в файле *.cs (в этом примере используется Activity1 как имя класса и Number как пространство имен);


  • Т.к. сборку бизнес-процесса необходимо будет зарегистрировать в CRM, то необходимо ее подписать цифровым ключом: щелкните правой кнопкой мыши по названию проекта в Solution Explorer — Properties — Signing — поставьте галку Sign the assembly — выберите в ниспадающем списке <New…> и введите какое-нибудь название для ключа;


  • Соберите сборку и опубликуйте ее в CRM с помощью Plugin Registration Tool. При этом не нужно создавать никаких шагов – просто зарегистрируйте новую Assembly.


На этом собственно все – шаг кастомного бизнес-процесса теперь доуступен через пользовательский интерфейс CRM, поэтому можно тестировать…

Тестирование

  • Создайте новый бизнес-процесс в CRM для объекта Бизнес-партнер;
  • В качестве есдинственного шага выберите, только что созданный наши шаг бизнес-процесса;
  • В его настройках укажите подставлять в лукап динамическое значение: запись текущего Бизнес-партнера;
  • Опубликуйте бизнес-процесс и вызовите событие на которое Вы его зарегистрировали (я «ручками» запусти). Перйдите на закладку бизнес-процессов на форме Бизнес-партнеров и дождитесь пока экземпляр бизнес-процесса отработает. Затем обновите (F5) запись Бизнес-партнера и увидите что кастомное поле autonumber заполнено уникальным номером 🙂




Отладка кастомных бизнес-процессов

… мало чем отдичается от отладки плагинов: чтобы отладить кастомный бизнес-процесс, Вы должны скопировать файл *.pdb из папки \bin\Debug Вашего C#-проекта в папку <папка установки CRM>\Server\bin\assembly на Вашем CRM сервере, установить контрольные точки в методе Execute и подключить к проекту Asynchronous service (CrmAsyncService.exe).

Если у Вас не получается обновить файл в папке assembly, потому что уже есть другая копия файла, перезапуститеть пул сайта CRM в IIS (или перезагрузите сам IIS: iisreset) или и/или перезапускают MS Dynamics CRM Asynchronous service.

Обновление сборок

После окончания разработки кастомного бизнес-процесс, Вы должны регистрировать его с помощью Plugin Registration Tool. Но тут есть маленькая загвоздка: когда Вы пересобираете (измененную) сборку Вашего бизнес-процесса и с помощью Plugin Registration Tool обновляете ее в CRM – внесенные изменения не появляются сразу же в CRM. Это потому что, старый код закэшировался. Чтобы обновить его достаточно перезапустить пул приложений MS CRM в IIS’е (ну, или на крайняк рестартнуть сам IIS) и/или перезапускают MS Dynamics CRM Asynchronous service.

Кастомные входящие/исходящие поля для бизнес-процессов

Описание того, как добавить входящие и исходящие поля в Ваш кастомный бизнес-процесс Вы можете найти в статье на MSDN: http://msdn.microsoft.com/en-us/library/cc151132.aspx

Свойства контекста плагинов и бизнес-процессов

https://docs.google.com/View?id=dcvd2dvh_534k95jhq&pli=1

П.С. существует более функционально развитый плагин реализующий автонумерацию в CRM – Счетчик.

Комментарии (2)
  • Игорь 05.03.2010

    Подскажите, пожалуйста, у меня есть workflow который создает объекты по триггеру. Мне необходимо, прежде чем создавать эти записи, проверить наличие дубликатов — записей с такими же полями ( поле owner и parentobject). Подойдет ли вариант решения — создание вишеуказанных полей в настройках шага, а затем с помощью query expression поиск дублей?
    p.s. жаль что стандартные правила проверки на дубли не отрабатываю в случае workflow

  • slivka_83 05.03.2010

    Не совсем понял задумку. Но в кастомном шаге Вы можете проверить все что угодно 🙂

*

code