Здравствуйте, изучил тему по ссылке. В частности последний пост.
Возник ряд вопросов:
- Как правильно использовать 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?