Вопрос

При параллельном сохранении Entity.Save() с использованием параллельных потоков Parallel.ForEach получаю ошибку:

System.Data.SqlClient.SqlException: A trigger returned a resultset and/or was running with SET NOCOUNT OFF while another outstanding result set was active.

Как установить NOCOUNT ON для объекта Entity при сохранении?

У меня такой же вопрос

12 комментариев

Проблема возникает из-за того, что в каком-то триггере в базе данных для таблицы, связанной со схемой Entity не установлен SET NOCOUNT ON.

Для решения проблемы Вам нужно определить, что это за триггер и внести в него изменения.

У меня, например, такая проблема воспроизводилась при попытке удалить бизнес-процесс из конфигураци и возникала для объекта Contact. Для этой таблицы в БД есть триггер завязанный на изменение (UPDATE), но в нем не было установлено SET NOCOUNT ON.

Возможно, что у Вас также проблема именно в этом триггере.

Суть ошибки я понимаю. Вопрос в другом имеем ли мы право вносить правки в системные триггер.

 

Добрый день, Игорь.

Это зависит от того, о каком триггере речь. Генерировался ли он автоматически ядром, или создан специально для этой таблицы разработчиками вручную.

В первом случае менять нежелательно, во втором — нужно будет быть осторожным при установке обновлений, если в пакете будет SQL-запрос обновления стандартного триггера.

Узнать, что это за триггер, можно, наблюдая запросы в профайлере.

Мотков Илья,

Илья, Добрый день.
проблема в базовых триггерах таблиц Contact и Account

Это стандартная логика, связанная с поиском дублей и полем-изображением. Из трёх триггеров на событии вставки и изменения работает только один, обновляющий изображение.

Если дорабатывать или отключать триггер не хотите и сбой наблюдается только после Ваших доработок, есть смысл их переделать на последовательное изменение записей. 

Коновалов Игорь пишет:
Суть ошибки я понимаю. Вопрос в другом имеем ли мы право вносить правки в системные триггер.

Да, исправления нужно внести обязательно, так как это ошибка. Уверенна, что никаких критических проблем не возникнет, однако сначала выполните эти исправления на тестовой базе, протестируйте, а потом переносите на прод.

Если это ошибка, тогда хотелось бы получить от поддержки Terrasoft решение.

Решением будет удаление некорректных сторонних доработок, которые ломают стандартную логику триггеров раздела. В конфигурации вообще использование Parallel.ForEach минимально, только один раз при работе с сервисом рассылок.

Илья, вопрос к триггерам таблицы
- Контрагент:
TRILqybnWrGVlAZ250EZZORaS6GAIU
TRILqybnWrGVlAZ250EZZORaS6GJAD
TRAccountID
- Контакт:
TRContactID
TRILE9Mk5tdkf4ii5Xu52IJW7JlLAD

Все они являются базовыми и все содержат строку
SET NOCOUNT ON

Игорь, это стандартные триггеры. Без крайней необходимости их менять не стоит. Адаптируйте свой код, чтобы он не вызывал сбоев.

Мотков Илья,

Я обрабатываю большой объем данных. Без применения

Parallel.ForEach данная выгрузка займет несколько дней.
Базовые триггеры прерывают данный процесс.

 

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

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте.
Место событий Entity, событие Saved, для которого был организован событийный подпроцесс. Последний со скриптом и сообщением которое запускает этот скрипт по событию. 
И вопрос, как в скрипте можно определить какие поля были изменены после сохранения, что бы в зависимости от этого в запустить свою логику?

У меня такой же вопрос

5 комментариев
Лучший ответ

Добавьте еще подписку на событие, перед сохранением.
Так же делаете подписку, скрипт.
В параметрах создайте нужные переменные нужных типов.
После чего в самом скрипте "перед сохранением", делайте так:
ContactOld = Entity.GetTypedOldColumnValue<Guid>("Contact");
Затем, в вашем скрипте "После сохранения" делайте сверку
if (ContactOld != Entity.GetTypedColumnValue<Guid>("Contact") { // }

Добавьте еще подписку на событие, перед сохранением.
Так же делаете подписку, скрипт.
В параметрах создайте нужные переменные нужных типов.
После чего в самом скрипте "перед сохранением", делайте так:
ContactOld = Entity.GetTypedOldColumnValue<Guid>("Contact");
Затем, в вашем скрипте "После сохранения" делайте сверку
if (ContactOld != Entity.GetTypedColumnValue<Guid>("Contact") { // }

Это можно сделать только в событийном процессе объекте перед сохранением записи

Entity.GetColumnOldValue("Name")

или

Entity.GetTypedOldColumnValue<String>("Name");

 

Добрый день!
На вход элемента скрипта обработки сохранения подается переменная ProcessExecutingContext context (https://monosnap.com/file/TP10NeOGRBkG4zXPFKxrVZM8Tvj4I6).
В ней есть public object ThrowEventArgs { get; set; }, которая приводится к объекту EntityAfterEventArgs. А в этом объекте уже есть массив измененных колонок: public EntityColumnValueCollection ModifiedColumnValues { get; set; }

Еще проще проверить что столбец изменен

var flag = Entity.GetChangedColumnValues().Any(col => col.Name == "Date");

Всем спасибо!

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

 В чем может быть проблема?

Процесс валится с ошибкой https://yadi.sk/i/OEAV92qdZHfeIA

Вот сам процесс:

Параметр:

 

У меня такой же вопрос

8 комментариев

А если просто:

AccountListParameter = query.GetEntityCollection(userConnection);

 

Rochefort Trappistes,

Процесс так даже не опубликуется

эт почему не опубликуется? параметры это поля класса (процесс это класс), а блок c# это метод, который имеет доступ к полям класса, как на чтение так и на запись.

снимите галочку "Интерпритируемый" в блоке с c# кодом (в расширенных настройках)

 

Rochefort Trappistes,

нет такой галочки

три точки - расширенный режим, как-то так

Rochefort Trappistes,

значит убрали её. какая ошибка при компиляции, если сетить значение в параметр через равно, как я писал выше?

 Rochefort Trappistes, см тут:

Начиная с версии 7.12.3 все создаваемые бизнес-процессы в bpm’online являются интерпретируемыми. Для обращения к значениям параметра процесса следует использовать методы get и set.

Миннекаев Айдар, дело в том, что в версии 7.13 в системе отключена возможность создания компилируемых бизнес-процессов, что делает невозможным работу с параметром EntityCollection. Параметр такого типа оставлен в системе для совместимости со старыми бизнес-процессами, которые работают в компилируемом режиме, а для новых — возможность использования компилируемых скрипт-тасков в БП отключена.

В последующих версиях системы запланирована поддержка EntityCollection и работы по внедрению этой поддержки, по словам разработчиков ядра системы, уже ведутся.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Коллеги всем доброго времени суток. Подскажите, кто нибудь работал с событийным слоем Entity.
Написал класс обработчик в качестве примера для обучения скомпилировал приложение упал сайт.
Вот пример класса обработчика:
 

using System;
using System.Web;
using Terrasoft.Core;
using Terrasoft.Core.Entities;
using Terrasoft.Core.Entities.Events;


namespace Terrasoft.Configuration
{
    [EntityEventListener(SchemaName = "Order")]
    public class OrderEntityEventListener : BaseEntityEventListener
    {

        private UserConnection _userConnection;

        private LogService _logService;
        public UserConnection UserConnection
        {
            get
            {
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                _userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection;
                if (_userConnection != null)
                {
                    return _userConnection;
                }
                return _userConnection;
            }

            set { _userConnection = value; }
        }        

        public LogService LogService { get => _logService; set => _logService = value; }

        public OrderEntityEventListener()
        {
            LogService = new LogService(UserConnection);
        }

        public override void OnInserting(object sender, EntityBeforeEventArgs e)
        {
            try
            {
                base.OnInserting(sender, e);
                
                var systemUserName = UserConnection.CurrentUser.ContactName;
                var systemUserId = UserConnection.CurrentUser.ContactId;
                LogService.RecInfo($"LOG:[OnInserting]:systemUserId: {systemUserId}; systemUserName:{systemUserName}");
            }
            catch (Exception exception)
            {
                LogService.RecInfo($"LOG:[OnInserting]:Exception: {exception.Message}; {exception.InnerException}");
            }

        }
    }
}

Подскажите, что тут не так? Если кто работал, скинте пожалуйста примеры.
Заранее благодарен

У меня такой же вопрос

1 комментарий
Лучший ответ

Добрый день, Алексей!

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {
            base.OnSaved(sender, e);
            var entity = (Entity) sender;
            var userConnection = entity.UserConnection;
        }

Ошибка заключается в том, что слушатели изменений сущностей, работают не только при изменении значений через карточку (в данном случае есть HttpContext) но и в фоновом режиме (процессы, планировщик - без HttpContext).

Добрый день, Алексей!

UserConnection необходимо получать из sender

Пример:

public override void OnSaved(object sender, EntityAfterEventArgs e) {
            base.OnSaved(sender, e);
            var entity = (Entity) sender;
            var userConnection = entity.UserConnection;
        }

Ошибка заключается в том, что слушатели изменений сущностей, работают не только при изменении значений через карточку (в данном случае есть HttpContext) но и в фоновом режиме (процессы, планировщик - без HttpContext).

Войдите или зарегистрируйтесь, чтобы комментировать

Здравствуйте!
Возникла проблема при копировании записей.
Использую следующий конструктор:
var newEntity = new Entity (entity);
Запись корректно создается, однако события объекта не срабатывают, в чем может быть проблема?

Полный код приведен ниже:

private void copyRouteDetail(Guid TemplateId, Guid TransRequestId) {
        EntitySchema schema = UserConnection.EntitySchemaManager.GetInstanceByName("SxRouteDetailTransRequest");
        EntitySchemaQuery esq = new EntitySchemaQuery(schema);
        esq.AddAllSchemaColumns();
        esq.UseAdminRights = false;
        esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal,
                                                        "SxRouteOptions", TemplateId));
        EntityCollection entities = esq.GetEntityCollection(UserConnection);
         foreach (Entity entity in entities)
        {
                var newEntity = new Entity (entity);
                newEntity.SetColumnValue("SxRouteOptionsId", null);
                newEntity.SetColumnValue("SxTransRequestId", TransRequestId);
                newEntity.UseAdminRights = false;
                newEntity.Save(false);
        }
}

У меня такой же вопрос

8 комментариев

Здравствуйте, данный вопрос будет решён в рамках Вашего обращения №0300267

"Пащенко Александр Сергеевич" написал:Запись корректно создается, однако события объекта не срабатывают, в чем может быть проблема?

Добрый день Александр!!!

я бы рекомендовал внимательно проанализировать все бизнес-процессы выполняемые в сохраняемом Entity, внимательно проанализировать сначало Глазами, а потом внимательно проанализировать в Дебагере, что происходит в вашей базе данных при сохранение, и найти место где возникает исключение, при котором запись создается, но не выполняются события.

я могу предположить что некорректно созданы бизнес-процессы главной и замещающих схем Entity.

Михаил, все процессы процессы построены корректно и работают стабильно.
Процитирую ответ техподдержки:
"Данное поведение проявляется по причине того что, при использования данного конструктора класса Entity свойства создаваемого объекта отвечающие за вызов событий проставляются как false. К сожалению, изменить данные свойства после создания объекта нельзя.

Данную задачу можно решить следующим образом:

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

Упростить задачу копирования значений колонок из одного объекта в другой можно используя метод public IEnumerable GetColumnValueNames(), который возвращает имена колонок объекта. Затем перебирая в foreach результат данного метода скопировать значения колонок."

При отладке, единственная разница, которую нашел - при использовании конструктора
var newEntity = new Entity(oldEntity)
у newEntity.Process=null,
у oleEntity.Process = не nulll, а ссылка на метод.

Михаил, все процессы процессы построены корректно и работают стабильно.
Процитирую ответ техподдержки:
"Данное поведение проявляется по причине того что, при использования данного конструктора класса Entity свойства создаваемого объекта отвечающие за вызов событий проставляются как false. К сожалению, изменить данные свойства после создания объекта нельзя.

Данную задачу можно решить следующим образом:

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

Упростить задачу копирования значений колонок из одного объекта в другой можно используя метод public IEnumerable GetColumnValueNames(), который возвращает имена колонок объекта. Затем перебирая в foreach результат данного метода скопировать значения колонок."

При отладке, единственная разница, которую нашел - при использовании конструктора
var newEntity = new Entity(oldEntity)
у newEntity.Process=null,
у oleEntity.Process = не nulll, а ссылка на метод.

"Пащенко Александр Сергеевич" написал:Данную задачу можно решить следующим образом:

var userConnection = context.UserConnection;
var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Account");
var newEntity = schema.CreateEntity(UserConnection);
newEntity.SetDefColumnValues();
//код установки значений колонок объекта
newEntity.Save();

Добрый день Александр!!!

так техническая поддержка вам предложила верный вариант решения вашей проблемы. Чем он вам не нравиться? что не устраивает?

Мне нужно сделать точную копию записи, в которой более 70 колонок, изменив всего 1 значение. Использовать для этого указанный выше конструктор считаю более целесообразным, но он некорректно работает. О варианте перебора всех колонок я знал. Сюда ответ продублировал на случай, если кому-то понадобится еще.

Спасибо за отклик!

Александр день добрый!!!

вы свою задачу можете вообще решить иструментами бизнес-процесса, и поидее это будет правильно. Подглядеть к примеру можно в бизнес-процесс  CreateInvoiceFromOrder . В данном бизнес процессе как раз происходит копирование продуктов из Заказа в Счет. Я бы пошел по данному пути.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

В процессе, в рамках одного "Задания-Сценария" идет цикл по строкам (продуктам).
По логике, в этом цикле идет запрос в другую сущность, и если ничего не возвращает, делает новую строку в той сущности.
Если находит, то делает апдейт найденной строки.

Вопрос: Если в списке строк попадутся обе ситуации, и при первом проходе будет создана нужная запись, то при втором проходе запрос ее не возвращает - это потому что entity.Save реально в базу изменения внесет только после окончания сценария, или даже всего процесса?
Или нужно просто ввести временной лаг после добавления записи, чтобы успела система все сохранить?

У меня такой же вопрос

1 комментарий

Ложная тревога, причина была в другом. save работает как надо

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте.
Создал объект на основе базового объекта. Добавил событие "После добавления записи", в нем пул и дорожку.
На дорожку поместил событие "Сообщение".Поведение.Сообщение = "BaseEntityInserted". Вообщем делал по аналогии с базовым объектом, но после сохранение записи БП не выполняется. Что я сделал не так?

У меня такой же вопрос

4 комментария

Здравствуйте!
После добавления записи рекомендуется использовать обычные процессы. Вы можете построить бизнес процесс, используя дизайнер процессов. Начальный элемент - сигнал. Настройки сигнала: объект - созданный Вами объект, событие - после добавления записи. Подробнее о бизнес процессах Вы можете прочитать здесь.

Да можно и так, но если я захочу добавить функционал не "После добавления записи", а перед сохранением, тут уже БП не поможет, что в таких случаях нужно делать?

В таких случаях:
1) Добавьте обработку события до "Сохранения записи"
2) Откройте диаграмму процесса
3) Добавьте элемент "Задание-сценарий" (этих элементов может быть несколько, они могут выполняться как последовательно, так и параллельно)
4) Пропишите все, что должно произойти в элементе "Задание-сценарий"

Вся исполняемая логика должна быть в элементе "Задание-сценарий".

"Дашкевич К." написал:

Здравствуйте.
Создал объект на основе базового объекта. Добавил событие "После добавления записи", в нем пул и дорожку.
На дорожку поместил событие "Сообщение".Поведение.Сообщение = "BaseEntityInserted". Вообщем делал по аналогии с базовым объектом, но после сохранение записи БП не выполняется. Что я сделал не так?

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

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте , есть несколько объектов Entity (родитель Базовый Объект) в которых есть каскадное \ связанное обновление реализованное на событиях. Скажите можно ли инициализировать изменения в одном из них, так чтобы для низ лежащих не проверялись права для того кто инициировал изменение в родителе.

Или вообще инициировать изменение или сохранение объекта Entity из под другого логина например?.

Права на объекты настроены на уровне записей.

Предметно:

Есть лист согласования который согласует заявление на отпуск, в котором должны поставить визы руководители и отдел кадров. У заявление на отпуск в детали есть записи указанных дней отпуска (Персональные дни), для каждого сотрудника (Контакт). В этих днях, есть кол-во использованных дней, которое по закрытие заявления рассчитываются. Права на изменение и добавление есть у отдела кадров, у сотрудника только на чтение, для того чтобы формировать заявление. Если лист согласование завизирван всеми участниками успешно то меняется заявление (изменяет состояние), и далее заявление на уровне объект (Entity) меняет те дни которые указанны в детали. И получается: чтобы всё прошло успешно - необходимо выдавать права на изменение на всё это хозяйство - всем кто должен подписать отпуск. Что не есть хорошо.

пример изменения по событие в entity

if (daysCount == daysUsed  &&  (daysUsed > 0)) {
   Entity.SetColumnValue("FreeDayStatusId", Terrasoft.Configuration.FreeDayStatusConst.used);
}

Я понимаю можно было написать update, или сделать на уровне SQL Server триггером, в первом случае придется воротить много одинаковых update со всех мест которые меняют объекты, во втором нагрузка на БД а не на IIS и условия изменения в данных не позволяют использовать триггер бд.

У меня такой же вопрос

1 комментарий

Если вместо UserConnection использовать UserConnection.AppConnection.SystemUserConnection, то права будут игнорироваться.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Добрый день!

Подскажите, где можно получить информацию классе Entity, и о его методах в частности? На удивление, в SDK этой информации не нашел :(

У меня такой же вопрос

3 комментария

Добрый день, Николай!
На данный момент класс Entity находится в процессе review.
Если у Вас есть точечные вопросы по какому-то методу – будем рады объяснить его предназначение с примерами использования.

С уважением, техподдержка «Террасофт»

Здравствуйте, Николай!

В папке с дистрибутивом есть папка Terrasoft.WebApp, в ней в папке bin лежат библиотеки (dll-файлы), которые вы можете добавить в проект в Visual Studio и затем посмотреть классы в данных библиотеках со всеми методами, свойствами, типами и так далее (скриншот во вложении). Либо можете посмотреть структуру любой другой программой для просмотра dll библиотек.

Александр, спасибо большое - то, что нужно!

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте!
Можно ли в JS отключить учет прав при update,insert c использованием ESQ?

У меня такой же вопрос

6 комментариев

Александр, ESQ задумывалась как раз в контексте работы с правами доступа.

Александр, без проверки вроде бы можно только на сервере, с клиентской части это довольно небезопасно.

Я понимаю, но есть задача,права в которой весьма противоречивы. Программно должно добавляться, а вручную нет. На сервер перенести задачу нельзя. Возможно ли это как-то реализовать?

Данная возможность отсутствует, для этого Вы можете использовать объект Select() на стороне сервера

Александр, можно на сервере создать конфигурационный веб-сервис и при необходимости посылать с клиента запрос на него. Таким образом получится реализовать задачу.

"Андрей Каспаревич" написал:

Александр, можно на сервере создать конфигурационный веб-сервис и при необходимости посылать с клиента запрос на него. Таким образом получится реализовать задачу.


Уже так и сделал. Спасибо.

Войдите или зарегистрируйтесь, чтобы комментировать