Как получить возвращаемое значение из запроса с помощью Dapper?
Я пытаюсь получить возвращаемое значение из запроса на вставку с помощью Dapper.
Вот как я пытаюсь заставить это работать:
// the query with a "returning" statement
// note : I have a trigger that sets the Id to a new value using the generator IF Id is null...
string SQL = "UPDATE OR INSERT INTO \"MyTable\" (\"Id\", \"Name\") " + "VALUES (@Id, @Name) RETURNING \"Id\"";
using (var conn = new FbConnection(MyConnectionString)) {
var parameters = new DynamicParameters();
parameters.Add("Id", null, System.Data.DbType.Int32);
parameters.Add("Name", "newName", System.Data.DbType.String);
// --- also add the returned parameters
parameters.Add("retval", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
// execute the query with Dapper....
conn.Execute(SQL, parameters);
// expecting the new ID here but it is ALWAYS null....!!!
var newId = parameters.Get<object>("retval");
}
Теперь, чтобы убедиться, что мой запрос в порядке и не является источником проблемы, я реализовал аналогичный код с моим фактическим соединителем (в данном случае Firebird) следующим образом:
using (var conn = new FbConnection(MyConnectionString)) {
FbCommand cmd = new FbCommand(SQL, conn);
cmd.Parameters.Add("Id", null);
cmd.Parameters.Add("Name", "newName");
FbParameter pRet = cmd.Parameters.Add("retval", FbDbType.Integer);
pRet.Direction = ParameterDirection.ReturnValue;
conn.Open();
cmd.ExecuteNonQuery();
// => the new value is NOT null here, it returns the correct id!!
var newId = Convert.ToInt32(pRet.Value);
conn.Close();
}
В чем моя ошибка в коде Dapper? Почему одна версия подходит, а другая НЕ? Я читал, что Dapper выполняет ExecuteNonQuery(), поэтому не ожидаю, что это будет причиной.
2 ответа
В returning
пункт действует как select
, поскольку он возвращает данные в сетке результатов. Таким образом, ваш запрос должен выполняться как запрос. Это также имеет то преимущество, что значительно упрощает вызывающий код:
var newId = conn.QuerySingle<int>(SQL, new { Id = (int?)null, Name = "newName" });
Если вам нужны дополнительные поля, это можно расширить, чтобы использовать настраиваемый тип возвращаемого значения, соответствующий возвращаемым столбцам, или кортеж значений. Например:
var row = conn.QuerySingle<MyTable>(SQL, new { Id = (int?)null, Name = "newName" });
или
var row = conn.QuerySingle<(int id, string name)>(SQL, new { Id = (int?)null, Name = "newName" });
- изменить Вы можете получить доступ к возвращаемым значениям
int iVal = row.Result.id;
string sVal = row.Result.name;
Самым большим недостатком Execute () Dapper является то, что он возвращает «количество затронутых строк» (обновлений, удалений и т. Д.) ... даже если все происходит в транзакции, которая после возникновения ошибки была отменена с помощью ROLLBACK. Возвращаемое значение по-прежнему содержит номер затронутой строки до отката, хотя транзакция не была зафиксирована. Ура !!
DynamicParameters() был более сложным, но работал. Но в тестах Moq я столкнулся с рядом исключений, которые я не мог легко разрешить.
Мое решение (похожее на решение Марка и Неггенбе) выполнило следующие шаги:
- В хранимой процедуре SQL вернуть целочисленное значение через,
SELECT -1 -- 0 for success, -1 for error note--> SQL-Returns (ie. RETURN(1)) are ignored for some reason.
- Используйте Dapper как таковой,
int result = conn.QueryFirst<int>(SProcName, new { id = req.Id, value = req.Value }, commandType: CommandType.StoredProcedure); note--> Other commands work as well with differing return types: QueryFirst: result = key/value where value=[return value] QueryFirst<int>: result = integer QuerySingle: Detailed by Marc and neggenbe's answer.
- Проверьте результат соответствующим образом, как в приведенных выше примерах.