Добрый день всем.

Использовал такую конструкцию

insert.Set(ColumnName, Column.SqlText($"( NEXT VALUE FOR [dbo].[" + SequenceName + "])"));

Чтобы вставлять в инсерте значения из последовательности. Сейчас такая конструкция не работает так как после обновления Column.SqlText() считается устаревшим.

Необходимо вставлять значения именно и вставляю в большом количестве. Триггер на БД не подходит, так как не всегда нужно это делать.

Есть ли альтернативы для применения такой схемы?

Нравится

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

Добрый день,

 

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

Terrasoft.Core.DB.Sequence sequence = new Terrasoft.Core.DB.Sequence(UserConnection, "CountBy1");
int result = sequence.GetNextValue();
Set<int>("SeqResult", result);
return true;

Этот код я использовал в тестовой скрипт-таске для проверки получения следующего значения sequence и его увеличения. Действительно, он работает, увеличивает значение и берет его же (нужно заменить CountBy1 на название Вашего sequence). То есть в Вашем коде нужно взять result и прокинуть в аргумент Set метода и будет ровно точно тот же результат, что и при использовании Column.SqlText. 

Показать все комментарии

Добавляю данные в бизнес-процессе через задание сценарий. Делаем многострочный Insert

var insCertificateData = new Insert(UserConnection).Into("NavCertificateData");
foreach (var entity in navCertificateData)
{
	insCertificateData.Values()
		.Set("NavCertificate", Column.Parameter(certificateId))
		.Set("NavProduct", Column.Parameter(entity.NavProduct ))
		.Set("NavDiametr", Column.Parameter(entity.NavDiametr))
		.Set("NavMarka", Column.Parameter(entity.NavMarka,))
		.Set("NavNippelAssembly", Column.Parameter(entity.NavNippelAssembly))
		.Set("NavQuantity", Column.Parameter(entity.NavQuantity))
		.Set("NavWeight", Column.Parameter(entity.NavWeight))
		.Set("NavMinUES", Column.Parameter(entity.NavMinUES))
		.Set("NavMaxUES", Column.Parameter(entity.NavMaxUES))
		.Set("NavMinLength", Column.Parameter(entity.NavMinLength))
		.Set("NavMaxLength", Column.Parameter(entity.NavMaxLength))
		.Set("NavDK", Column.Parameter(entity.NavDK))
		.Set("NavBend", Column.Parameter(entity.NavBend))
		.Set("NavKTP", Column.Parameter(entity.NavKTP))
		.Set("NavCompanyGrafit", Column.Parameter(entity.NavCompanyGrafit));
}
insCertificateData.Execute();

В основном все типа string. В значении NavNippelAssembly иногда приходит EmptyString. И в этом случае получаем ошибку 

"Для параметра \"P5\" со значением null необходимо указать тип данных"

Собственно есть Column.Parameter(Object,DataValueType). Полагаю, что надо его использовать, но как указать этот самый DataValuType? Тип строка  

Нравится

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

Алексей Следь,

Посмотрите обсуждение в этой теме.

Для установки null можно использовать Column.Const(null)

Николай Кузьмин,

Так у меня ж не всегда null и 5 полей, где может быть string.Empty

Алексей Следь,

Посмотрите обсуждение в этой теме.

Алла, спасибо, я там как раз приводил похожий пример, только в нынешнем случае сравнение будет не с Guid.Empty, а с String.Empty.

 .Set("IndustryId", ((IndustryIdParameter!=Guid.Empty)?Column.Parameter(IndustryIdParameter):Column.Const(null)))

Ну, или проверять условие и если не выполняется, не производить добавление к формируемому запросу этого Set, тогда в SQL-запросе вообще не будет упоминания этого пустого поля.

Мда, как всегда решение на поверхности лежало. 

Александр, по мне правильней тогда делать сравнение не со string.Empty, а проверять stringIsNullOrEmpty(). А т.к. табличка у меня нуллы не поддерживает, то все пришло к такой конструкции

.Set("NavProduct", (string.IsNullOrEmpty(entity.NavProduct)? Column.Parameter(string.Empty) : Column.Parameter(entity.NavProduct)))

 

Алексей, если всё так, как описали, то зачем вообще два раза писать Column.Parameter и проверять на Empty? Можно только на null при помощи «??», как предложили тут:

.Set("NavProduct", Column.Parameter(entity.NavProduct ?? string.Empty))

 

Александр, учитывая источник данных, лучше проверять на Null и Empty. В любой момент Null может стать пустой строкой.

Так пустая строка в итоге и пишется, её же не надо на такую же заменять, можно сразу в параметр.

Показать все комментарии

Приветы,

 

Подскажите плиз, как правильно сделать, создал объект наследуемый от базового и добавил поле BpCandidate как строка 50 символов в конструкторе объекта.

 

Пробовал сделать поле типа справочник и тогда оно становится Guid в коде

В следующем коде было вместо TEXT, у поля BpCandidate указан GUID

Так вот в консоли хрома видел ошибку уровня c#, а именно System.String не могу преобразовать в System.Guid и как быть? 

 

Удалив столбец, пересохранив и добавив его снова как строка 50 символов и вернув ТЕКСТ вместо ГУИД все заработало, но как быть с гуидами в инсерт запросами???

 

insertCount: function (candId) {

                this.logStep('insertCount started...');

                var insertQuery = this.Ext.create("Terrasoft.InsertQuery", {

                    rootSchemaName: "BpCandidateCall"

                });

                insertQuery.setParameterValue("CallCount",

                    1,

                    this.Terrasoft.DataValueType.INTEGER);



                insertQuery.setParameterValue("BpCandidate",

                    candId,

                    this.Terrasoft.DataValueType.TEXT);

                var dt = new Date();

                insertQuery.setParameterValue("CallDate",

                    dt,

                    this.Terrasoft.DataValueType.DATE);

                insertQuery.setParameterValue("CallTime",

                    dt,

                    this.Terrasoft.DataValueType.TIME);

                insertQuery.execute(function () {

                    //this.loadActivities();

                }, this);

                this.logStep('insertCount finished...');

            },

Нравится

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

А что если колонка в базе будет GUID, а в коде выше DataValueType.TEXT?

Показать все комментарии

Коллеги, приветствую.

Есть определенный интерес поизучать классы Select, Insert, Update, EntitySchemaQuery и подробнее
разобраться, как с ними работать.

Чтобы это было проще и быстрее мне бы хотелось иметь возможность работать с ними из MS Visual Studio.

Например, запрос из статьи Использование EntitySchemaQuery для построения запросов к базе данных:

// Создание экземпляра запроса, добавление в запрос колонок и источника данных.
Select selectQuery = new Select(UserConnection)
                    .Column("Id")
                    .Column("Name")
                    .From("Contact");
// Выполнение запроса к базе данных и получение результирующего набора данных.
using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection())
{
    using (IDataReader reader = selectQuery.ExecuteReader(dbExecutor))
    {
        while (reader.Read())
        {
            // Обработка результатов запроса.
        }
    }
}

Собственно, вопрос в конфигурировании класса UserConnection, как его использовать "вне платформы", не в режиме отладки?

Например, в методе Main:

static void Main(string[] args)
{
}

В элементе "Задание- сценарий" я могу получить его таким образом:

var userConnection = Get("UserConnection");

В Web- сервисе WCF:

var userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];

Был бы весьма признателен за информацию.

Спасибо.

--
С уважением, Алексей Быков.

Нравится

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

Здравствуйте.
Если сайт у вас развернуть on-site, то есть, поднят на своем IIS сервере:
Тогда у вас есть возможность написать свой, к примеру, веб-сервис, или даже скрипт-сценарий, и пользуясь статьей по отладке,
http://academy.terrasoft.ru/documents/docs/technic/SDK/7.6.0/ServerCode…
ставить точки останова, и изучать поведение серверного ESQ.
Здесь главное, что бы код был частью системы, поэтому его и нужно писать как часть системы (веб-сервис, бизнес-процесс, и.т.д.), а не сторонние dll,exe,итд.
С автономной программы этого сделать не получится.

Если же сайт on-demand, то есть, развернут как http://имя-сайта.bpmonline.com/
То такой возможности у Вас нет, и со стороннего процесса (программы), Вы никак не обратитесь к ядру системы.
Из сторонних программ написанных Вами в Visual Studio, Вы можете разве что обратится к уже написанным веб-сервисам в рамках структуры сайта, либо же по протоколу OData,
http://academy.terrasoft.ua/documents/docs/technic/SDK/7.4.1/WorkWithBp… что, конечно же, совсем не запросы ESQ.

Александр, спасибо большое за ответ!

Показать все комментарии

Доброго времени суток, коллеги. При интеграции BPM с внешней БД возникла необходимость вставлять даннные из карточки BPM в таблицу на связанном сервере (Firebird 2.0). Для работы с таблицами связанного сервера содали представления. С селектами из этих представлений никаких проблем не было. Но вот с инсертами и апдейтами возникла проблемка - после инсерта(апдейта или делита) транзакции подвисали минут на 5 и выполнить очередной запрос к этому представлению было невозможно.
Нашли выход из этой ситуевины:
1. Создали на связанном сервере хранимую процедуру (пишет данные в таблицу и возвращает одно значение).
2. На sql сервере в представлении создали триггер который срабатывает на инсерт, пинает хранимку на связанном сервере и передает параметры в эту хранимку.
3. В карточку BPM добавили кнопку и повесили на нее скрипт которые инсертит данные из полей карточки в представление.
После данные манипуляций все работает отлично, данные инсертятся, транзакции не подвисают.

Но вот возник вопросик. Как я написал в первом пункте, хранимка возвращает одно значение и мы не можем приложить ума как его перехватить чтобы вернуть это значение в текстовое поле в карточку BPM. Ниже код хранимки, триггера и скрипта инсерта.

Код хранимки.

BEGIN

INSERT INTO NAPRAVLENIE(NAPRAVLENIE.NAPRAVLENIE_NUM,NAPRAVLENIE.USLUGA_1) VALUES (:NUM,:U1);
NEW_OUTPARAM=gen_id(BP_GEN,1);

  SUSPEND;
END

Код триггера.

USE [BPMonline]
GO
/****** Object:  Trigger [dbo].[IOI_napr]    Script Date: 03/11/2015 17:56:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[IOI_napr]ON [dbo].[napr]
INSTEAD OF INSERT
AS
BEGIN
declare @U1 varchar(250)
declare @NUM varchar (250)
declare @NAP_NUM varchar (250)
SET @U1=(SELECT i.USLUGA_1 FROM inserted i)
SET @NUM=(SELECT i.NAPRAVLENIE_NUM FROM inserted i)
execute ('select * from BP_nap_ins('''+@NUM+''','''+@U1+''')') at DMS
 
END;

Код скрипта инсерта кнопки.

var U1=Page.TextEdit5.Value; // для теста передаем только 1 значение
var insert = new Insert(UserConnection).Into("napr").Set("USLUGA_1", Column.Parameter(U1)).Execute();
        return true;

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

каким оброзом можно запихнуть возвращаемое значение в переменную и использовать ее дальше в BPM ?
Спасибо что дочитали :)

Нравится

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

если в скрипт кнопки добавить

Page.TextEdit14.SetValue(insert);

то в TextEdit14 пишется 1 (единица), но передоваемое значение не равно 1.

Попробуйте указанный код, вместо Insert

var selectSQL = new CustomQuery(UserConnection, sqlText);
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
	using(var dr = selectSQL.ExecuteReader(dbExecutor)) {
		while (dr.Read()) {
			PreviousMonth = dr.GetValue(0).ToString();
		}
	}
}

Спасибо за ответ, пошли немного другим путем. Запихнули текст триггера еще в одну хранимку, и через OPENQUERY пнули хранимку на связанном сервере. Таким макаром удалось вернуть значение в переменную. Сейчас бытаюсь разобраться как вызвать хранимку из формы.

может кому не сложно написать коды вызова хранимки:

в хранимку передаем значение из поля TextEdit5.
вернуть значение нужно в TextEdit14

почитал немного тут http://www.community.terrasoft.ru/blogs/7708

вот, что наваял:

var U1=Page.TextEdit5.Value;
//var NAP_NUM = null;
var dataValueTypeManager = (DataValueTypeManager)UserConnection.AppManagerProvider.GetManager("DataValueTypeManager");
var proc = new StoredProcedure(UserConnection, "BNS_to_DMS");
StoredProcedure.WithParameter(U1);
StoredProcedure.WithOutputParameter("NAP_NUM", dataValueTypeManager.GetInstanceByName("Text"));
using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection()) {
        dbExecutor.StartTransaction(System.Data.IsolationLevel.ReadUncommitted);
        Page.DataSource.ActiveRow.Save();
        StoredProcedure.Execute(dbExecutor);}
SetCloseStatus(1);
//Connector.DBEngine.ExecuteCustomSQL(SQLText);
//var insert = new Insert(UserConnection).Into("napr").Set("USLUGA_1", Column.Parameter(U1)).Execute();
 	Page.TextEdit14.SetValue(NAP_NUM);
	return true;

но валится много ошибок при компиляции.

PS: извиняйте что туплю, я не программист

код хранимки

USE BPMonline;
IF OBJECT_ID ( 'dbo.BPM_to_DMS', 'P' ) IS NOT NULL 
    DROP PROCEDURE dbo.BPM_to_DMS;
GO
CREATE PROCEDURE dbo.BPM_to_DMS
    @U1 varchar(250),
    @NAP_NUM varchar (250) OUTPUT
    --@U2 varchar (250) 
  AS 
 
    SET NOCOUNT ON;
 
 set @NAP_NUM = (select * from OPENQUERY (dms,'select * from BP_nap_ins(''+@U1+'')'))
 
 GO

итак, удалось запустить хранимку, передать ей значение в U1, но никак не могу разобраться как вернуть из нее значение в TextEdit14. необходимое значение возвращается в NAP_NUM.

код вызова хранимки:

var U1=Page.TextEdit5.Value;
//var NAP_NUM;
var dataValueTypeManager = (DataValueTypeManager)UserConnection.AppManagerProvider.GetManager("DataValueTypeManager");
var storedProcedure = new StoredProcedure(UserConnection, "BPM_to_DMS");
storedProcedure.WithParameter(U1);
storedProcedure.WithOutputParameter("NAP_NUM", dataValueTypeManager.GetInstanceByName("Text"));
using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection()) {
        dbExecutor.StartTransaction(System.Data.IsolationLevel.ReadUncommitted);
        storedProcedure.Execute(dbExecutor);
				dbExecutor.CommitTransaction();
}
 
 	//Page.TextEdit14.SetValue(NAP_NUM);
	return true;

можно хотябы пример похожий (в примере с поиском дублей ничего не понятно)

PS: когда все допилю обещаю написать полный мануал по вызову хранимой процедуры на связанном сервере firebird 2.0

Добрый день. А почему Вы не используете тот пример, что я вам скинул? Им тоже можна "дергать" хранимые процедуры и получать значения

"Олейников Владимир Владимирович" написал:

Добрый день. А почему Вы не используете тот пример, что я вам скинул? Им тоже можна "дергать" хранимые процедуры и получать значения


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

там все очень просто. UserConnection - Переменная которая хранит в себе параметры подключения.
sqlText - сюда можно запихнуть любой SQL запрос, в том числе и вызов хранимой процедуры.

while (dr.Read()) {
PreviousMonth = dr.GetValue(0).ToString();
}

"dr" - сохраняет в себе структуру возвращенных данных

"Олейников Владимир Владимирович" написал:

там все очень просто. UserConnection - Переменная которая хранит в себе параметры подключения.

sqlText - сюда можно запихнуть любой SQL запрос, в том числе и вызов хранимой процедуры.

while (dr.Read()) {

PreviousMonth = dr.GetValue(0).ToString();

}

"dr" - сохраняет в себе структуру возвращенных данных


спасибо, будем пробовать

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

забал сказать что хранимка возвращает не просто число, а текст, по сути номер направления ( пример 15326/2015)

"Олейников Владимир Владимирович" написал:

там все очень просто. UserConnection - Переменная которая хранит в себе параметры подключения.

sqlText - сюда можно запихнуть любой SQL запрос, в том числе и вызов хранимой процедуры.

while (dr.Read()) {

PreviousMonth = dr.GetValue(0).ToString();

}

"dr" - сохраняет в себе структуру возвращенных данных

Владимир, ругается на PreviousMonth,

The name 'PreviousMonth' does not exist in the current context
DMSEditPage.cs
2918

PreviousMonth - это локальная переменная, которая создается и туда записывается значение. Можете вместо этого кода вписать
следующий код
Page.TextEdit14.SetValue(dr.GetValue(0).ToString());
только вместо "0" подставте нужный индекс. Посмотрите какой он по счету в выборке в запросе

"Олейников Владимир Владимирович" написал:

PreviousMonth - это локальная переменная, которая создается и туда записывается значение. Можете вместо этого кода вписать

следующий код

Page.TextEdit14.SetValue(dr.GetValue(0).ToString());

только вместо "0" подставте нужный индекс. Посмотрите какой он по счету в выборке в запросе


Владимир, попробовал подставить Page.TextEdit14.SetValue(dr.GetValue(0).ToString());

скрипт отрабатывает но в TextEdit14 ничего не возвращает(пробовал разные индексы(0 1 2), а вообще запись по счету первая).

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

var U1=Page.TextEdit5.Value;
//var result = 5;
var dataValueTypeManager = (DataValueTypeManager)UserConnection.AppManagerProvider.GetManager("DataValueTypeManager");
var storedProcedure = new StoredProcedure(UserConnection, "BPM_to_DMS");
storedProcedure.WithParameter(U1);
storedProcedure.WithOutputParameter("NAP_NUM", dataValueTypeManager.GetInstanceByName("Text"));
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
        dbExecutor.StartTransaction(System.Data.IsolationLevel.ReadUncommitted);
       // storedProcedure.Execute(dbExecutor);
	{
        using(var dr =storedProcedure.ExecuteReader(dbExecutor)) {
                while (dr.Read()) {
                     Page.TextEdit14.SetValue(dr.GetValue(0).ToString());
 
                }
        }
}
			dbExecutor.CommitTransaction();
 
}
 
 
	return true;

Профайлер перехватывает выполнение и показывает что в @NAP_NUM вернулось нужное значение

Попробуйте в дебаге посмотреть что вам возвращает переменная "dr" и все увидите

var dataValueTypeManager = (DataValueTypeManager)UserConnection.AppManagerProvider.GetManager("DataValueTypeManager");
var proc = new StoredProcedure(UserConnection, "BNS_to_DMS");
StoredProcedure.WithParameter(U1);
StoredProcedure.WithOutputParameter("NAP_NUM", dataValueTypeManager.GetInstanceByName("Text"));
using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection()) {
        dbExecutor.StartTransaction(System.Data.IsolationLevel.ReadUncommitted);
        Page.DataSource.ActiveRow.Save();
        StoredProcedure.Execute(dbExecutor);}
SetCloseStatus(1);
//Connector.DBEngine.ExecuteCustomSQL(SQLText);
//var insert = new Insert(UserConnection).Into("napr").Set("USLUGA_1", Column.Parameter(U1)).Execute();
        Page.TextEdit14.SetValue(NAP_NUM);
        return true;

Вместо этого кода напишите мой

"Олейников Владимир Владимирович" написал:

var dataValueTypeManager = (DataValueTypeManager)UserConnection.AppManagerProvider.GetManager("DataValueTypeManager");

var proc = new StoredProcedure(UserConnection, "BNS_to_DMS");

StoredProcedure.WithParameter(U1);

StoredProcedure.WithOutputParameter("NAP_NUM", dataValueTypeManager.GetInstanceByName("Text"));

using (DBExecutor dbExecutor = UserConnection.EnsureDBConnection()) {

        dbExecutor.StartTransaction(System.Data.IsolationLevel.ReadUncommitted);

        Page.DataSource.ActiveRow.Save();

        StoredProcedure.Execute(dbExecutor);}

SetCloseStatus(1);

//Connector.DBEngine.ExecuteCustomSQL(SQLText);

//var insert = new Insert(UserConnection).Into("napr").Set("USLUGA_1", Column.Parameter(U1)).Execute();

        Page.TextEdit14.SetValue(NAP_NUM);

        return true;

Вместо этого кода напишите мой


Владимир, простите что туплю, но я не знаю как через ваш код вызвать хранимку и передать в нее параметр, межете пример привести? если не сложно

вот что выдает дебагер

var sqlText = string.Format("ProcName {0},{1}", "value1", "value2")

Объявляем переменную, в которой хранится вызов хранимой процедуры с переметрами "value1", "value2"

var selectSQL = new CustomQuery(UserConnection, sqlText);
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
        using(var dr = selectSQL.ExecuteReader(dbExecutor)) {
                while (dr.Read()) {
                        PreviousMonth = dr.GetValue(0).ToString();
                }
        }
}

хранимая процедура вызывается как и любая SQL функция здесь

"Олейников Владимир Владимирович" написал:

хранимая процедура вызывается как и любая SQL функция здесь


спасибо за ответ, вот что вышло,

var U1=Page.TextEdit5.Value;
var Proc = string.Format("BPM_TO_DMS {0},{1}", "U1", "NAP_NUM");
	var selectSQL = new CustomQuery(UserConnection, Proc);
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
        using(var dr = selectSQL.ExecuteReader(dbExecutor)) {
                while (dr.Read()) {
                        Page.TextEdit14.SetValue(dr.GetValue(1).ToString());
                }
        }
}
return true;

но при выполнении ничего не происходит, дебагер выдал все переменные по null, профайлер вы дал просто строку "BPM_TO_DMS U1,NAP_NUM" куда копать?

var Proc = string.Format("BPM_TO_DMS {0},{1}", "U1", "NAP_NUM");

Вы не коректно написали, UI Надо писать без кавычек, это имя переменной, в которой хранится значение передаваемой в хранимку переменной.

"Олейников Владимир Владимирович" написал:

Вы не коректно написали, UI Надо писать без кавычек, это имя переменной, в которой хранится значение передаваемой в хранимку переменной.


Владимир, если написать таким образом то просит объявить переменную NAP_NUM, если ее объявить то просит записать туда какаое-нить значение. Если передать туда 2 значения то процедура не срабатывает т.к. в процедуре ожидается 1 параметр входящий и 1 исходящий, а таким образом мы передаем туда 2 входящих. Может подскажите каким образом обявить NAP_NUM исходящим параметром? Спасибо.

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

var Proc = string.Format("BPM_TO_DMS {0},{1} ...", "U1", "NAP_NUM", ...);

"Олейников Владимир Владимирович" написал:

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

var Proc = string.Format("BPM_TO_DMS {0},{1} ...", "U1", "NAP_NUM", ...);


Владимир, это я понял, но мне нужно передать в хранимку 1 параметр, и хранимка должна вернуть 1 параметр. для этого в хранимке объявлен переменная NAP_NUM OUTPUT (в которую возвращается результат работы хранимки). елси вызывать хранимку через var proc = new StoredProcedure то там можно указать какой из параметров входящий а какой выходящий. Может вы подскажете каким образом в вашем скрипте указать что NAP_NUM это OUTPUT параметр? спасибо за помощь

В конце хранимки сделать выборку нужного вам параметра

DECLARE @d INT = 0
-- Какие-то действия с переменной
SELECT  @d
Показать все комментарии

Подскажите пожалуйста. Вопрос у меня по работе с БД.
Необходимо осуществить добавления в БД не повторяющихся данных из файла. Как получить данные из файла и добавить я разобрался. Добавление делаю с помощью insert:

if (????????){
var insert = new Insert(UserConnection)
  .Into("ConfItemsCode")
  .Set("Name", Column.Const(str));
  insert.Execute();
}

Но передо добавлением в БД мне надо проверить есть ли в этой таблице уже такая запись. Делаю это так:
var select = new Select(UserConnection)
  .Column("Name")
  .From("ConfItemsCode")
  .Where("Name").IsEqual(str); // пробовал и IsLike

str - это переменная типа string - данный из файла.
Вопрос: Как мне узнать есть ли в select какие -либо записи. Пробовал RowCount, HasCondition. Подскажите как это сделать правильно. Желательно получить int или bool для того чтобы проверить а только потом добавлять.

Нравится

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

Здравствуйте.

Проще всего воспользоваться EnitySchemaQuery классом:

string Str = "test";
 
 
var manager = UserConnection.EntitySchemaManager;
var query = new EntitySchemaQuery(manager, "Account");
var currentAccountId = query.AddColumn(query.RootSchema.PrimaryColumn.Name);
var accountPhone = query.AddColumn("Name").Name;
query.Filters.Add(query.CreateFilterWithParameters(FilterComparisonType.Equal, "Name", Str));
var queryResult = query.GetEntityCollection(UserConnection);
if (queryResult.Count == 0) 
{
       var insert = new Insert(UserConnection)
  .Into("ConfItemsCode")
  .Set("Name", Column.Const(str));
  insert.Execute();
}

Дмитрий, спасибо. Все получилось

Показать все комментарии

Добрый день! Следующая конструкция успешно работала

ContractDataset.Edit();
ContractDataset.ValAsFloat('Amount') = Amount;
ContractDataset.ValAsFloat('BasicAmount') = BasicAmount;
ContractDataset.Post();

т.е. пытаюсь отредактировать запись датасета договора с уже существующим заголовком (заголовок сформирован при импорте из Excel значением по умолчанию), выдает следующие

[09.05.21 10.35.06.698] (E) Ошибка выполнения метода 'SelfOnDatasetAfterPost'. Ошибка сохранения записи. Оригинальное сообщение об ошибке: validation error for column Title, value "*** null ***"
The insert failed because a column definition includes validation constraints.
Error Code: 27
INSERT INTO "tbl_Contract" ("ID", "ContractTypeID", "ContractStatusID", "OwnerID", "StartDate", "DueDate", "BillingFrequencyID", "Amount", "BasicAmount", "ModifiedOn", "ModifiedByID", "AllUpgr", "DocWeek1", "DocWeek2", "DocWeek3", "DocWeek4", "DocWeek5", "CreatedOn", "CreatedByID")
VALUES (:ID, :ContractTypeID, :ContractStatusID, :OwnerID, :StartDate, :DueDate, :BillingFrequencyID, :Amount, :BasicAmount, CURRENT_TIMESTAMP, '{94EDBC92-596F-4957-BA05-96A682F739E5}', :AllUpgr, :DocWeek1, :DocWeek2, :DocWeek3, :DocWeek4, :DocWeek5, CURRENT_TIMESTAMP, '{94EDBC92-596F-4957-BA05-96A682F739E5}') «Call Stack»

ошибок в скрипте нет, т.к. конструкция

ContractDataset.Edit();
ContractDataset.ValAsFloat('Amount') = Amount;
ContractDataset.ValAsFloat('BasicAmount') = BasicAmount;
ContractDataset('Title') = 'Договор';
ContractDataset.Post();

успешно выполняется.

Насколько я правильно понимаю проблема в кодировке заголовка?

TS X25 3/3/0/42 Firebird

Нравится

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

Добрый день, Павел!

На сколько я могу судить из текста ошибки, она говорит о том, что в БД поле Title NOT NULL, но идет попытка вставить в это поле значение NULL. Нужно или NOT NULL убрать в дизайнере таблицы, или данные откорректировать.

А почему вставка идет, если запись редактируется? И меняются два поля, а вставка идет на все? Вы часом не из администратора этот скрипт выполняете?

Вставка наверное идет потому что датасет пустой.

Да, действительно, поле Title уже заполнено, в скрипте я лишь пытаюсь заполнить поля Amount и BasicAmount, поле Title я вообще не трогаю, оно должно остаться таким как есть

Датасет не пустой, сам так сначало думал, но повторюсь, конструкция

ContractDataset.Edit();
ContractDataset.ValAsFloat('Amount') = Amount;
ContractDataset.ValAsFloat('BasicAmount') = BasicAmount;
ContractDataset('Title') = 'Договор';
ContractDataset.Post();

нормально выполняется на нужной записи

А какой запрос идет если использовать такую конструкцию? Посмотрите если можете профайлером (для MSSQL).

Не получится, у меня firebird. А вообще и правда интересно почему идет insert, если я редактирую запись?

если я не ошибаюсь, то в IBExpert пункт меню Tools, подпункт SQL Monitor может вам помочь

Инсерт идет если датасет пустой. Попробуйте у него вызвать PageRecordsCount() перед вызовом Edit(). Если вернет не 0, то... этого не может быть потому что этого не может быть никогда :)
Посмотрите также состояние датасета передвызовом Edit()

Павел, Underscore a.k.a. задал верный вопрос:

"Underscore a.k.a. _" написал:Вы часом не из администратора этот скрипт выполняете?

Вы часом не из администратора этот скрипт выполняете????!!!

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

а событие OnAfterPost - пустое?
Возможно Post вашего Edit'a вызывает какой-то Append?

И что все-таки возвращают PageRecordsCount(), IsActive и State перед вызовом Edit()?

попробуйте заменить строку на

ContractDataset.ValAsStr('Title') = "Договор";

знаю, что отличий почти нет, но попробовать стоит.

Уточние, пожалуйста, в чем отличие ненужно от нужной записи, о которой вы упомянули в посте:

"нормально выполняется на нужной записи"

---Underscore a.k.a.

Перед вызовом edit()

IsActive = False
PageRecordsCount() = 0

"Карло Сергей" написал:Павел, Underscore a.k.a. задал верный вопрос:
"Underscore a.k.a. _" написал:Вы часом не из администратора этот скрипт выполняете?

Коллеги, у меня вчера была похожая проблема... Запускал из администратора (в чем разница, где запускать скрипт?)

Нужно было в CashFlow проставить одинаковое назначение, прописал скрипт:

function Main() {
 
var ds = Services.GetNewItemByUSI('ds_Cashflow');
ds.Open(); 
var R = ds.RecordsCount;
Log.Write(2, R);
while (!ds.IsEOF) {
    ds.Edit();
    ds('Subject') = 'Продажа Товара';
    Log.Write(2, ds('ID'));
    ds.Post();
    ds.GotoNext();
}
ds.Close();
}

Выдало сообщение

Что еще более интересно, количество записей в датасете: 1

Проверил: все фильтры отключены, в MS SMS

select count(ID) from tbl_Cashflow
-----------
9609
 
select count(ID) from tbl_Cashflow where AutocalcAmount is null
-----------
0

Обновил в SQL, но все же интересно - почему в датасете только 1 запись?
TS CRM X25 3.3.0.51

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Всем спасибо! Проблема решена.

Вместо

ContractDataset.Edit();
ContractDataset.ValAsFloat('Amount') = Amount;
ContractDataset.ValAsFloat('BasicAmount') = BasicAmount;
ContractDataset.Post();

Написал:

ContractDataset.Open();
ContractDataset.Edit();
ContractDataset.ValAsFloat('Amount') = Amount;
ContractDataset.ValAsFloat('BasicAmount') = BasicAmount;
ContractDataset.Post();
ContractDataset.Close();

Все заработало.

Но вообще я думал, что перед Dataset.Edit() не нужно писать
Dataset.Open()

"Павел Крышкин" написал:IsActive = False
PageRecordsCount() = 0

Ну вот, я же говорил, что у Вас датасет пустой.
Edit() начинает редактирование текущей записи. Если не было открытия датасета, то записей в нем нет.

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

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

"Underscore a.k.a. _" написал:В администраторе почему-то собираются неправильные запросы. Возможно это сделано для сборки запроса в предпросмотре текста запроса в дизайнере запросов. Но если это так, то автору идеи нужно тоже какую-нибудь каку сделать :)

С этим понятно, а почему в датасете ода запись?

--
www.it-sfera.com.ua
Terrasoft Solution Partner

RecordsCount шлет запрос запрос в базу. Гляньте что за запрос идет если скрипт выполнять из администратора.

"Underscore a.k.a. _" написал:RecordsCount шлет запрос запрос в базу. Гляньте что за запрос идет если скрипт выполнять из администратора.

Спасибо за совет!
Кто-то из программеров забыл убрать:

SELECT TOP 1
[tbl_Cashflow].[ID] AS [ID],
...

В фильтры смотрели, а в TOP - все были уверены, что все гуд!
Исправили :)

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Остается баг с update неясен...

--
www.it-sfera.com.ua
Terrasoft Solution Partner

"Виталий Ковалишин aka samael" написал:Остается баг с update неясен...

Похоже это не баг а фича:)

Благо, что SQL пока не подводит:

UPDATE tbl_Cashflow
SET Subject = 'Продажа Товара'

--
www.it-sfera.com.ua
Terrasoft Solution Partner

Эсли датасет закрытый или пустой -- то на Edit отрабатывает Append.

"Александр Кравчук" написал:Эсли датасет закрытый или пустой -- то на Edit отрабатывает Append.

Но в датасете 1 строка была! + я ж его открывал

...
var ds = Services.GetNewItemByUSI('ds_Cashflow');
ds.Open(); 
...

--
www.it-sfera.com.ua
Terrasoft Solution Partner

"Виталий Ковалишин aka samael" написал:Остается баг с update неясен...

Ну ведь написали:
"Underscore a.k.a. _" написал:В администраторе почему-то собираются неправильные запросы. Возможно это сделано для сборки запроса в предпросмотре текста запроса в дизайнере запросов.

Это именно так. Все связано с тем что если в адиминистраторе мы работаем через Main - то у всех обьектов Connector.IsDesigning = true - в таком случае все объекты ведут себя по другому, в частности датасет всегда возвращает все поля.
В этом случае нужно делать окно, запустать это окно через tscrm.exe /wnd=vashe_okno и на кнопочку в нем выполнять Ваше действие. :) Я именно так всегда и делаю.

Спасибо Александр, за детальное объяснение!

--
www.it-sfera.com.ua
Terrasoft Solution Partner

"Александр Кравчук" написал:В этом случае нужно делать окно

Ну или получается что можно с IsDesigning поиграться. У всех сервисов это свойство доступно и на запись.

"Underscore a.k.a. _" написал:Ну или получается что можно с IsDesigning поиграться. У всех сервисов это свойство доступно и на запись.


  Не выйдет, я уже пробовал.  

Точно , изменение этого свойства у датасета и инсертквери никчему не привело. Но у окон оно вроде работает. Ка-то этот IsDesigning через гланды сделан.

Тот IsDesigning который влияет на отображение всех колонок в Dataset'е не вынесен наружу.

А на что влияет IsDesigning , который вынесен? И почему бы его не сделать тем, который влияет?

Так исторически сложилось. Пока до этого руки не дошли. 

Показать все комментарии