...

Не обновляется контекст/данные после записи/сохранения в объект и последующим чтением из объекта

Тема в разделе "Вопросы по функционалу", создана пользователем alexei_dp, 3 апр 2018.

  1. alexei_dp

    alexei_dp New Member

    Добрый день!

    Столкнулся с проблемой при сохранении данных в объект (справочник) и последующим чтением из этого объекта - информация при чтении не обновляется .

    Процесс организован для добавления, редактирования и удаления записей справочника по следующему алгоритму:
    1) чтение всего из объекта в контекст;
    2) в таблице контекста производится добавление, удаление или правка записи/ей;
    3) при переходе по кнопке "Сохранить и продолжить" должен отрабатывать сценарий, который:
    а) очищает объект (справочник);
    б) записывает все из контекста в объект;
    в) очищается контекст;
    г) чтение записанного (в пункте б) ) из объекта в контекст

    Проблема возникает на шаге 3г - при чтении данные остаются такими же как и до изменения/сохранения, но при этом в самом объекте информация изменена.

    Складывается впечатление, что данные читаются из какого-то кеша, а не из объекта.

    При разнесении сценария на 2 независимых - "сохранение в объект" и "чтение из объекта" проблемы такой нет. Однако такое разнесение не подходит - все должно отрабатывать одной кнопкой.

    Как побороть?

    Пояснение на возможный вопрос "Зачем чистить контекст после записи и читать то, что и только что очистили?":
    "В пользовательской задаче присутствуют две контекстные таблицы: одна для редактирования (прямое представление записей справочника), вторая - для представления записей сгруппированно (есть поля типа "многое ко многим") для более наглядного представления и предусматривает только чтение без правок."

    /// <summary>
    /// Sohranitj_i_otobratj_main2
    /// </summary>
    /// <param name="context">Контекст процесса</param>
    public virtual void Sohranitj_i_otobratj_main2 (Context context)
    {
    try {
    // сохранение ===============================================================
    //****************
    //
    //очищаем справочник основной
    EntityManager<NomenklaturaITOsnovnaya>.Instance.DeleteAll();
    //
    foreach (var el in context.AktualjnayaNomenklatura) {
    //
    var Nomenklatura_Save = EntityManager<NomenklaturaITOsnovnaya>.Create ();
    //
    Nomenklatura_Save.Naimenovanie = el.Tovar.ToString () + " " + el.Proizvoditelj.ToString () + " " + el.TipModelj.ToString ();
    Nomenklatura_Save.Proizvoditelj = el.Proizvoditelj;
    Nomenklatura_Save.TipModelj = el.TipModelj;
    Nomenklatura_Save.Tovar = el.Tovar;
    Nomenklatura_Save.CenaDoll = el.CenaDoll;
    Nomenklatura_Save.OsnovnayaNomenklatura = el.OsnovnayaNomenklatura;
    //
    Nomenklatura_Save.Save ();
    //
    }
    //
    context.AktualjnayaNomenklatura.Clear ();
    context.SootvetstvieNomenklatury.Clear();
    //
    //*********************


    // отбор в контекст ====================================================================

    var Nomenklatura_All = EntityManager<NomenklaturaITOsnovnaya>.Instance.FindAll ();
    //
    context.KolichestvoZapisey2 = Nomenklatura_All.Count ();
    //
    // работаем с контекстром первой таблицы (для редактирование товара)
    //
    // грузим все из объекта
    foreach (var el in Nomenklatura_All.ToList() ) {
    var Nomenklatura_Load = EntityManager<P_TipovovayaTehnika_AktualjnayaNomenklatura>.Create ();
    //
    Nomenklatura_Load.OsnovnayaNomenklatura = el.OsnovnayaNomenklatura;
    //
    Nomenklatura_Load.Naimenovanie = el.Naimenovanie;
    Nomenklatura_Load.Proizvoditelj = el.Proizvoditelj;
    Nomenklatura_Load.TipModelj = el.TipModelj;
    Nomenklatura_Load.Tovar = el.Tovar;
    Nomenklatura_Load.CenaDoll = el.CenaDoll;
    //
    context.AktualjnayaNomenklatura.Add (Nomenklatura_Load);
    }

    //
    // работаем с контекстом сопоставляемой таблицы
    // ----------------------------------------------------------------------------

    var Tovary = EntityManager<NomenklaturaITNazvanie>.Instance.FindAll ();
    // отбираем все что есть
    // наполняем по каждой группе товаров
    foreach (var el in Tovary) {
    var SootvetstvieNomenklatury_Load = EntityManager<P_TipovovayaTehnika_SootvetstvieNomenklatury>.Create ();
    //
    SootvetstvieNomenklatury_Load.Tovar = el;
    //
    var Nomenklatura_Load = EntityManager<NomenklaturaITOsnovnaya>.Instance.FindAll ();
    // отбираем все
    //
    // идем по всем позициям номенклатуры и сопоставляем с группами
    foreach (var el_load in Nomenklatura_Load) {
    // если товар соответствует группе
    if (el_load.Tovar == el) {
    // если есть флаг основной номенклатуры
    if (el_load.OsnovnayaNomenklatura == true) {
    SootvetstvieNomenklatury_Load.OsnovnayaNomenklatura.Add (el_load);
    }
    // если есть флаг дополнительной номенклатуры
    if (el_load.OsnovnayaNomenklatura == false) {
    SootvetstvieNomenklatury_Load.RezervnayaNomenklatura.Add (el_load);
    }
    }
    }
    // сохраняем
    context.SootvetstvieNomenklatury.Add (SootvetstvieNomenklatury_Load);
    }
    // ----------------------------------------------------------------------------

    }

    catch (Exception e) {
    context.Text += e.Message + System.Environment.NewLine;
    context.Text += e.StackTrace + System.Environment.NewLine;
    }
    }
     
  2. arkarimov

    arkarimov Member

    Дело в особенностях сценариев, и в том, как вы их используете: Все данные сбрасываются в БД после завершения сценария а не в момент вызова Save, соотв. FindAll находит актуальные записи в БД на момент сценария (до завершения транзакции)
    Частное решение - не задействовать FindAll - вы в сценарии все равно очищаете всю таблицу, а значит все данные для неё в сценарии же и доступны, сделайте так
    Код:
    //очищаем справочник основной
    EntityManager<NomenklaturaITOsnovnaya>.Instance.DeleteAll();
    //Создаем список в памяти, зачем нам БД
    var Nomenklatura_All = new List<NomenklaturaITOsnovnaya>();
    foreach (var el in context.AktualjnayaNomenklatura) {
    //
    var Nomenklatura_Save = EntityManager<NomenklaturaITOsnovnaya>.Create ();
    //
    Код заполнения Nomenklatura_Save
    //
    Nomenklatura_Save.Save ();
    Nomenklatura_All.Add(Nomenklatura_Save);
    //
    }
    Но сама реализация в принципе видится сомнительной -справочник не темповая таблица, а такой вариант не масштабируется (если завтра понадобится 2 параллельных процесса для разных групп номенклатуры?)
    Возможно лучше было бы посмотреть в сторону блоков, в них и доступ можно отрегулировать, и наполнять по разному и относиться они будут только к конкретному экземпляру, а значит не мешать другим экземплярам и процессам
     
    1 это нравится
  3. alexei_dp

    alexei_dp New Member

    Спасибо!
    Блоков данных в чем (в каком объекте)?

    Предполагалось, что справочник служит хранилищем для обмена информацией между процессами. И как казалось на данный момент наиболее оптимальный для межпроцессного обмена.
    С одной стороны - процесс, который поддерживает актуальность данных, а с другой - процесс, который эти данные только читает.
    Однако если предположить, что идут одновременно несколько экземпляров процессов поддержки актуальности (т.е. с операциями чтения и записи в справочник), то не совсем ясно как не потерять данные в справочнике.
    Каким другим образом организовать межпроцессный обмен?
     
  4. arkarimov

    arkarimov Member

    Блок это специальная переменная которая выглядит как таблица https://www.elma-bpm.ru/kb/help/Platform/content/Designer_Context_var_block_create_index.html
    В первом сообщении не идет речи о обмене между процессами, способ обмена может быть разный - зависит от решаемой задачи, можно меняться сообщениями, можно писать напрямую в контекст другого процесса, можно через общие справочники.
    Меня покоробила идея удалять все записи справочника и писать в него "с нуля" - обычно все же записи справочника могут быть дополнены вне данного процесса (пользователями, другими процессами) и все эти изменения будут потеряны получается.
    В вашем случае надо наверное отталкиваться от исходной задачи - что нужно в итоге получить, и какие операции выполняются.
    Например добавив номенклатуре признак "Актуальность" и не очищая весь справочник забирать только актуальное, по факту редактирования данных пользователем - изменять признак актуальности, для измененых позиций находить соотв. запись справочника соответствия номенклатуры (кстати он не чистится - будут записи с битыми ссылками) и в них править
     
  5. alexei_dp

    alexei_dp New Member

    вот как раз в этом справочнике и предполагается хранение только актуальной информации...

    Нужен справочник с актуальной информацией, данные из которого будут доступны на чтение для любого экземпляра любого процесса.
    Актуальность поддерживается отдельным бизнес-процессом.
    Ввиду того, что актуальны все записи, не увидел большого смысла фиксировать и перезаписывать (обновлять) только те, что изменились.
    Собственно, поэтому и удаляю все и перезаписываю всеми данными (и измененными и не измененными).

    Или, все-таки, стоит как-то изменить подход?
     
  6. arkarimov

    arkarimov Member

    Поясню чем мне не нравится идея удалять записи (особенно восстанавливая их потом)
    В процессе "читателе" размещаются ссылки на справочник. В какой то момент вы захотите просмотреть процесс от позавчера - и увидите там битые ссылки (хотя ассортимент там рассматривался актуальный!). Еще хуже - если вы заведете процесс или справочник "Заказ клиента" и в нем разместите ссылки на номенклатуру - после удаления уже не узнаете что же заказывал клиент.
    Смысл как минимум в сохранении целостности. Все процессы которые ссылались (читали из) на удаленные записи справочников потеряют часть информации.
     
    1 это нравится
  7. alexei_dp

    alexei_dp New Member

    Спасибо за пояснения!
    Даже не задумывался об этом. Влияние принципов учетной системы...

    Для окончательного понимания особенностей...
    Если вызывать несколько процедур из одной общей процедуры (при этом каждая из них обращается к одной и той же записи объекта), например вот так:

    public virtual void Sohranenie_hodataystv_v_reestr_Plus_Status (Context context)
    {
    Sohranenie_hodataystv_v_reestr (context);
    Status_Soglasovano_FinDep (context);
    }
    выполнение сброса данных в БД будет после завершения вложенной процедуры (например Sohranenie_hodataystv_v_reestr) или основной - Sohranenie_hodataystv_v_reestr_Plus_Status ?
    Или, как вариант, расположить их последовательно на схеме друг за другом - отработает после перехода от одного блока на схеме к другому?
    Или ELMA оптимизирует это на своем уровне и объединяет в одну логику?
     
  8. arkarimov

    arkarimov Member

    Вот тут этот вопрос хорошо разбирается.
    http://yambr.ru/2017/09/15/unit-of-work-и-elma-bpm/
    Коротко - транзакция выполняется в рамках одного элемента БП, неважно сколько там внутри нарисовано процедур и функций.
     
    1 это нравится
  9. alexei_dp

    alexei_dp New Member

    Спасибо!
     

Поделиться: