// Создание экземпляра запроса, добавление в запрос колонок и источника данных.
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"];
Здравствуйте.
Если сайт у вас развернуть 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.
Есть некоторый запрос, который возвращает дату платежа и дату последней активности по платежу:
SELECT
ContactId AS debtorId,
t1.CreatedOn AS activityDate,
t2.CreatedOn AS paymentDate
FROM Activity AS t1 INNER JOIN UsrPayments AS t2
ON t1.ContactId = t2.UsrDebtorId
WHERE t1.CreatedOn = (
SELECT max(CreatedOn) FROM Activity
WHERE t1.ContactId = t2.UsrDebtorId
);
И есть некоторый код C#, где с помощью подзапроса я хочу определить дату последней активности по платежу, аналогично тому, как я это делаю выше на native SQL:
var selectNewPayments = (Select)new Select(userConnection)
.Column("t1", "ContactId")
.Column("t1", "CreatedOn")
.Column("t2", "CreatedOn")
.From("Activity").As("t1")
.Join(JoinType.Inner, "UsrPayments").As("t2")
.On("t1", "ContactId").IsEqual("t2", "UsrDebtorId")
.Where("... здесь подзапрос и обращение к функции max() ...")
as Select;
"Шамшин_Олег" написал:А можно то же самое, только не Select'ом а с помощью ESQ?
1. Делаете один esq запрос на содержимое подзапроса, выбираете из него, список Id, или чего вам нужно, в оригинальном посте подзапрос выбирал CreatedOn. Формируете массив этих CreatedOn.
2. В колбеке после возврата результата подзапроса, формируете новый esq запрос, с фильтрацией нужной колонки, в оригинальном посте CreatedOn на вхождение в массив ранее выбранных CreatedOn.
Т.к. фильтрация createColumnInFilterWithParameters спокойно проверит вхождение колонки и в массив.
Пример оторванный от контекста esq, рассматривает только создания фильтра, но такой фильтр подставленный в esq будет работать и в esq: http://www.community.terrasoft.ru/forum/topic/11497#comment-50859
Здравствуйте, изучил тему по ссылке. В частности последний пост.
Возник ряд вопросов:
- Как правильно использовать dbExecutor для избежания утечки памяти?
- В каких именно случаях нужно пользоваться указанным в теме подходом?
- Всегда ли нужно оборачивать методы Execute() и ExecuteScalar() классов Select, Insert, Update в using, если нет, то в каких случаях это не обязательно?
"Венжик Игорь" написал:Часто возникает ситуация когда необходимо сделать специфичечкий запрос к БД, который невозможно или сложно воспроизвести с помощью бизнес-сущностей. Например запросы к таблицам прав доступа, к системным таблицам. (Не законченная статья...)
В таких случая необходимо строить кастомные запросы:
Select select =
new Select(UserConnection)
.Column("Id")
.Column("SysSchemaId")
.Column("Name")
.Column("SysSchemaManagerName")
.Column("SysSchemaFolderId")
.Column("MetaDataModifiedOn")
.From("VwSysSchemaInSolution")
.Where("SysSolutionId").IsEqual(new QueryParameter("solutionId", userConnection.Solution.Id))
.And("SysSchemaId").In(schemas)
.And("SysSchemaStateInSolution").IsNotEqual(Column.Const((int)StoringObjectState.Deleted))
.And().OpenBlock("LockedById").IsNull()
.Or("LockedById").IsEqual(new QueryParameter("currentUserId", userConnection.CurrentUser.Id))
.CloseBlock()
as Select;
Для выполнения запросов к БД в нашей системе есть специальный объект DBExecutor. Экземпляр которого содержится в каждом UserConnection-е. То есть одному пользователю всегда доступен только один экземпляр DBExecutor-а, и пользователь не может сам создавать новые экземпляры.
Что бы получить экземпляр DBExecutor-а необходимо вызвать метод UserConnection.EnsureDBConnection().
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
}
Для вызова всегда необходимо использовать конструкцию using!
Давайте раcсмотрим несколько вариантов работы
Допустим нам необходимо выполнить простой запрос:
Select select =
new Select(UserConnection)
.Column("Name")
.From("Contact")
.Where("City").IsEqual(Column.Parameter("Киев"))
Получение значения из первой строки, первого столбца выборки
Есть два способа
var name = select.ExecuteScalar();
или
string name;
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
name = select.ExecuteScalar(dbExecutor);
}
Оба способа вернут один и тот же результат. Если вы выполняете одиночный запрос тогда используйте первый вариант, он является оберткой над вторым. Второй вариант, с передачей dbExecutor-а, будет полезен если выполнять несколько запросов к БД в рамках одной транзакции.
Получение списка значений:
List names = new List();
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
using (var reader = select.ExecuteReader(dbExecutor)) {
while(reader.Read()) {
int columnOrdinal = reader.GetOrdinal("Name");
names.Add(reader.GetString(columnOrdinal));
}
}
}
Выполнение запроса в рамках транзакции:
Оба запроса в базу будут выполнены в рамках транзакции и в случае свала транзакция откатится.
List names = new List();
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
>> dbExecutor.StartTransaction();
using (var reader = select.ExecuteReader(dbExecutor)) {
while(reader.Read()) {
int columnOrdinal = reader.GetOrdinal("Name");
names.Add(reader.GetString(columnOrdinal));
}
}
insert.Execute(dbExecutor);
>> dbExecutor.CommitTransaction();
}
Транзакция начинается вызовом метода dbExecutor.StartTransaction и заканчивается вызовом CommitTransaction или RollbackTransaction. В случае когда выполнение вышло за область видимости блока using и CommitTransaction не был вызван, происходит автоматический откат транзакции. Таким образом нет необходимости оборачивать транзакцию в try/catch блок, т.к. если был свал во время выполнения транзакция автоматически откатится.
Внимание! В текущей реализации даже если не передавать dbExecutor в метод Execute(), все равно запрос будет выполнен в текущей транзакции, если такая существует. Но для избежания сложностей в будущем, всегда при выполнении нескольких запросов в рамках транзакций - всегда передавайте dbExecutor в методы Execute, ExecuteReader, ExecuteScalar.
Методы Execute() и ExecuteScalar() классов Select, Insert, Update в using оборачивать не нужно.
Для Select-ов ExecuteScalar или Execute, который принимает анонимный метод - также не стоит оборачивать в using.
Для Select-а, который использует конструкцию List names необходимо использовать Using:
List names = new List();
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
using (var reader = select.ExecuteReader(dbExecutor)) {
while(reader.Read()) {
int columnOrdinal = reader.GetOrdinal("Name");
names.Add(reader.GetString(columnOrdinal));
}
}
}
Добрый день! Давно и сильно интересует вопрос, каким образом обернуть в транзакцию событийный процесс объекта при сохранении/удалении? Для гарантированной согласованности данных в разных сущностях, когда изменения в одном объекте влекут изменения в другом связанном.
Столкнулся с некоторыми трудностями чтения из базы данных с помощью объекта DBExecutor.
Есть простой кастомный запрос:
var userConnection = Get("UserConnection");
Guid addedRecordId = Get("addedRecordId");
var selectQuery = new Select(userConnection)
.Column("UsrName")
.From("UsrDebt").As("ud")
.Where("UD", "Id")
.IsEqual(Column.Parameter(addedRecordId));
var userConnection = Get("UserConnection");
Guid addedRecordId = Get("addedRecordId");
var selectQuery =
new Select(userConnection)
.Column("UsrName")
.From("UsrDebt").As("ud")
.Where("UD", "Id")
.IsEqual(Column.Parameter(addedRecordId));
// Выполнение запроса к базе данных и получение результирующего набора данных.
using (DBExecutor dbExecutor = userConnection.EnsureDBConnection())
{
using (IDataReader reader = selectQuery.ExecuteReader(dbExecutor)) // строка 48
{
while (reader.Read())
{
// Обработка результатов запроса.
}
}
}
Получаю такую ошибку:
Как я могу получить значения из базы данных с помощью объекта DBExecutor?
Есть некоторый простой бизнес- процесс, инициируемый событием- сигналом "Добавление записи в таблицу". При каждом добавлении записи я получаю ее уникальный идентификатор, например -
F6F4725A-C7C6-4AA2-BE50-E4878ACB7D4B
Я выполняю простой запрос из MS SQL Server Management Studio -
SELECT
UsrInDebtId
FROM
UsrDebt
WHERE
Id='F6F4725A-C7C6-4AA2-BE50-E4878ACB7D4B';
Получаю какой- то результат.
Аналогично из кода C#
...
myConnection.Open();
SqlCommand myCommand = new SqlCommand("...", myConnection);
SqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read()) {
...
}
...
Уникальный идентификатор я сохраняю в параметре addedRecordId -
Guid addedRecordId = Get("addedRecordId");
Далее я хочу использовать класс Select из пространства имен Terrasoft.Core.DB -
Может быть, существует возможность получить строковое представление уникального идентификатора из события- сигнала и далее уже программно создать Guid, который и передать в предикат?..
var newSelect = new Select(CurrentUserConnection)
.Column("Table1", "Column1").As("Column1")
.From("Table1").As("Table1")
.Where("Table1", "Column2").IsNotEqual( ... ) as Select;
И имеется некоторая коллекция значений, к примеру:
List myList = new List();
Вопрос в следующем: Каким образом можно в условие запроса передать список значений моей коллекции (будь то Array, ArrayList, List, Dictionary, Hashtable, SortedList или другое - значения не имеет)?
Условно говоря, мне нужно мою коллекцию представить в виде типа "Select", чтобы можно было использовать в месте отмеченном 3-мя точками, - это если следовать сигнатуре этих методов, согласно описанию из SDK (www.terrasoft.ru/bpmonlinesdk/). Но как это сделать? Прошу помощи!
Виталий, для этого нужно использовать конструкцию .Not().In(массив_или_список_идентификаторов) .
Пример использования (схема EditSynchUserInSynchRole):
var ldapGroupsIds =new List<QueryParameter>();
var ldapUserLoginAttribute = SysSettings.GetValue(UserConnection, "LDAPUserLoginAttribute").ToString();using(var ldapUtils =new LdapUtilities(UserConnection)){
var usersCollection = ldapUtils.GetUsersAttributesByFilter(ldapUserLoginAttribute +"="+ ldapEntry, new[]{"distinguishedName"});
var userDn = string.Empty;
foreach (SearchResultEntry user in usersCollection){
userDn = user.DistinguishedName;break;}
var ldapGroupIdentityAttribute = SysSettings.GetValue(UserConnection, "LDAPGroupIdentityAttribute").ToString();
var groupsCollection = ldapUtils.GetGroupsAttributesByFilter("member="+ userDn, new[]{ldapGroupIdentityAttribute});
foreach(SearchResultEntry group in groupsCollection){
ldapGroupsIds.Add(new QueryParameter(ldapUtils.IdentityAttributeToString(group.Attributes[ldapGroupIdentityAttribute][0])));}}if(ldapGroupsIds.Count<1){returntrue;}
var delete=new Delete(UserConnection).From("SysUserInRole").
Where("SysUserId").IsEqual(Column.Parameter(userId)).
And().Exists(new Select(UserConnection).
Column("Id").From("SysAdminUnit").
Where("SysUserInRole", "SysRoleId").IsEqual("SysAdminUnit", "Id").
And("SynchronizeWithLDAP").IsEqual(Column.Parameter(true)).
And("LDAPEntryId").Not().In(ldapGroupsIds));delete.Execute();
Если же передавать массив, то так (схема AdministrativeGrantedRightsGridPage):
object[] objectParameters =new object[adminUnitCollection.Count];for(int i =0; i < adminUnitCollection.Count; i++){
objectParameters[i]= adminUnitCollection[i];}}
var entitySchemaManager = Page.UserConnection.EntitySchemaManager;
var rightsSchema = entitySchemaManager.GetInstanceByName("SysAdminUnitGrantedRight");
var select =new Select(Page.UserConnection)
.Column(rightsSchema.Name, "Id")
.Column(rightsSchema.Name, "GrantorSysAdminUnitId")
.Column("Grantor", "Name").As("GrantorSysAdminUnitName")
.From(rightsSchema.Name)
.InnerJoin("SysAdminUnit").As("Grantor").On("Grantor", "Id").IsEqual(rightsSchema.Name, "GrantorSysAdminUnitId")
.Where(rightsSchema.Name, "GranteeSysAdminUnitId").In(Column.Parameters(objectParameters))
.OrderByAsc("Grantor", "Name") as Select;
Я тоже думал, что только массивы из Object, но оказалось, что не только массивы.
В первом примере обратите внимание, что коллекция из элементов именно типа QueryParameter.
Верно ли это? Тогда я не смогу указать оператор "In()" для колонки, содержащую тип "Guid", т.к. я буду проверять наличие текущей записи в коллекции guids, а у нее QueryParameter содержит тип "string". Что-то тут нечисто... Подскажете, как быть?:confused:
Как сконструировать условие OR в Select`е? Есть запрос на TSQL:
SELECT *
FROM
[dbo].[WorkUnitAction]
WHERE
[WorkUnitId] = @p1
AND [Id] > @p2
AND [CreatedOn] @p3
AND NOT [ActionResultId] IS NULL
AND ([GroupId] > @p4 OR GroupId IS NULL)
ORDER BY
[CreatedOn] DESC
Часто возникает ситуация когда необходимо сделать специфичечкий запрос к БД, который невозможно или сложно воспроизвести с помощью бизнес-сущностей. Например запросы к таблицам прав доступа, к системным таблицам. (Не законченная статья...)
В таких случая необходимо строить кастомные запросы:
Для выполнения запросов к БД в нашей системе есть специальный объект DBExecutor. Экземпляр которого содержится в каждом UserConnection-е. То есть одному пользователю всегда доступен только один экземпляр DBExecutor-а, и пользователь не может сам создавать новые экземпляры.
Что бы получить экземпляр DBExecutor-а необходимо вызвать метод UserConnection.EnsureDBConnection().
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
}
Для вызова всегда необходимо использовать конструкцию using!
Давайте раcсмотрим несколько вариантов работы
Допустим нам необходимо выполнить простой запрос:
Select select =
new Select(UserConnection)
.Column("Name")
.From("Contact")
.Where("City").IsEqual(Column.Parameter("Киев"))
Получение значения из первой строки, первого столбца выборки
Есть два способа
var name = select.ExecuteScalar<string>();
или
string name;
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
name = select.ExecuteScalar<string>(dbExecutor);
}
Оба способа вернут один и тот же результат. Если вы выполняете одиночный запрос тогда используйте первый вариант, он является оберткой над вторым. Второй вариант, с передачей dbExecutor-а, будет полезен если выполнять несколько запросов к БД в рамках одной транзакции.
Получение списка значений:
List<string> names = new List<string>();
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
using (var reader = select.ExecuteReader(dbExecutor)) {
while(reader.Read()) {
int columnOrdinal = reader.GetOrdinal("Name");
names.Add(reader.GetString(columnOrdinal));
}
}
}
Выполнение запроса в рамках транзакции:
Оба запроса в базу будут выполнены в рамках транзакции и в случае свала транзакция откатится.
List<string> names = new List<string>();
using (var dbExecutor = UserConnection.EnsureDBConnection()) {
>> dbExecutor.StartTransaction();
using (var reader = select.ExecuteReader(dbExecutor)) {
while(reader.Read()) {
int columnOrdinal = reader.GetOrdinal("Name");
names.Add(reader.GetString(columnOrdinal));
}
}
insert.Execute(dbExecutor);
>> dbExecutor.CommitTransaction();
}
Транзакция начинается вызовом метода dbExecutor.StartTransaction и заканчивается вызовом CommitTransaction или RollbackTransaction. В случае когда выполнение вышло за область видимости блока using и CommitTransaction не был вызван, происходит автоматический откат транзакции. Таким образом нет необходимости оборачивать транзакцию в try/catch блок, т.к. если был свал во время выполнения транзакция автоматически откатится.
Внимание! В текущей реализации даже если не передавать dbExecutor в метод Execute(), все равно запрос будет выполнен в текущей транзакции, если такая существует. Но для избежания сложностей в будущем, всегда при выполнении нескольких запросов в рамках транзакций - всегда передавайте dbExecutor в методы Execute, ExecuteReader, ExecuteScalar.