Разработка
23
Июл
7

«Уникальная» авто-нумерация

С появлением транзакций CRM2011 появилась возможность создать исключительно силами плагина (и поддерживаемым способом) создать автонумератор, который гарантированно будет генерировать уникальные номера.

Рассмотрим как это сделать:

  • Создайте новый объект autonumber, в котором будут храниться счетчики. В этом объекте должны быть следующие поля:
    • new_name: название счетчика;
    • new_counter (целочисленное): собственно инкриминирующийся счетчик;
    • new_nowdate (дата/время): служебное поле-пустышка, которое необходимо для «блокировки» записи счетчика и гарантирования того, что другие экземпляры плагина не попытаются одновременно добраться до счетчика.
  • Создайте запись счетчика и определите для него начальное значение;
  • Создайте в объекте (в данном примере это Организация), в записях которого необходимо вести счет поле new_counter (в него будем подставлять значение счетчика);
  • Создайте плагин со следующим кодом:
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Query;
    
    namespace autonumber
    {
        public class autonumber : IPlugin
        {
            private Guid counterGUID = Guid.Empty;
            public autonumber(string unsecureConfig)
            {
                counterGUID = new Guid(unsecureConfig);
            }
    
            public void Execute(IServiceProvider serviceProvider)
            {
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                Entity entity = (Entity)context.InputParameters["Target"];
    
                try
                {
                    IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
    
                    // Обновляем поле-пустышку в записи счетчика
                    Entity counter = new Entity("new_autonumber");
                    counter["new_autonumberid"] = counterGUID;
                    counter["new_nowdate"] = DateTime.Now;
                    service.Update(counter);
    
                    // Возвращаем текущее значение счетчика
                    counter = service.Retrieve("new_autonumber", counterGUID, new ColumnSet("new_counter"));
    
                    // Приращаем счетчик
                    int number = Convert.ToInt32(counter["new_counter"]) + 1;
    
                    // Обновляем числовое значение в записи счетчика
                    counter["new_counter"] = number;
                    service.Update(counter);
    
                    // Подставляем счетчик в создаваемую запись
                    entity["new_counter"] = number;
                }
                catch (FaultException<OrganizationServiceFault> ex)
                {
                    throw new InvalidPluginExecutionException("Ошибка: ", ex);
                }
            }
        }
    }
    

    Этот плагин делает следующее:

    • Получает из небезопасных параметров GUID записи счетчика;
    • Обновляет в записи счетчика поле дата/время. Это самый важный шаг в этом плагине. Т.к. произошло обновление записи, текущий экземпляр плагина получил на нее «монополию» (в БД на нее создалась блокировка) и не «отпустит» пока плагин полностью не завершит свою работу. Если в это время запись будет «занята», то текущий экземпляр плагина будет ждать пока она не освободится;
    • Затем запрашиваем текущее значение счетчика и приращаем его на единицу;
    • Обновляем счетчик;
    • Подставляем счетчик в создаваемую запись (в поле new_counter).
  • Создайте в CRM запись счетчика, дайте ей имя и изначальный номер;
  • Зарегистрируйте плагин на pre-create нужного объекта и подставьте в небезопасный параметр GUID счетчика.

На этом все 🙂 У нас есть уникальная нумерация 🙂

P.S. Если при создании записи произойдет сбой и произойдет откат транзакции, таким же образом откатится и приращение счетчика. Поэтому в последовательности присвоенных номеров не будет «разрывов».




Комментарии (7)
  • Михаил 23.07.2012

    Уникальной нумерации не получится в случаях:
    1. Массовая загрузка данных, когда много данных вливается в несколько потоков, но это можно решить мутексом.
    2. Наличие кластера из серверов (пункт 1 с этим уже не справится).
    3. Работа с офлайн клиентом.

  • Антон 23.07.2012

    Спасибо за отличный и очень полезный сайт!
    Вопрос не по теме: Как настроить перенос кастомных полей (напр. Скидка) из Продуктов для предложения в Продукты для заказ при стандартном преобразовании Предложения в Заказ?

  • slivka_83 23.07.2012

    2Михаил

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

  • slivka_83 23.07.2012
  • slivka_83 23.07.2012

    2Михаил

    А для третьего пункта я бы предложил:
    1. Для каждого пользователя вести свой уникальный номер: 1, 2, 3 и т.д.;
    2. Когда срабатывает плагин, то производить проверку работает ли он офлайн. И если да, то добавлять к уникальному номеру суфикс из кода пользователя (например «_1»).

  • Михаил 23.07.2012

    По поводу блокировки транзакции — блокируется запись с которой выполняется операция. В этом случае это клиент.
    А запись счётчика тут каким боком?

  • slivka_83 23.07.2012

    Если бы блокировалсь только запись Контакта, то каким бы образом производился откат транзакции? 🙂 Ведь в случаи ошибки все изменения (и с сущностью на которую он зарегистрирован и со всем остальнм) произведенные плагином откатываются.

*

code