#c# #.net #dapper
#c# #.net #dapper
Вопрос:
Система, над которой я сейчас работаю, использует хранимые процедуры для всего доступа к данным. В данный момент я изучаю Dapper (пока он выглядит великолепно), но мне было интересно, могу ли я использовать объект DynamicParameters, созданный с использованием шаблона, но сделать один из параметров параметром вывода. Например:
SP:
CREATE PROCEDURE InsertPerson
@ID int Output,
@Name varchar(100),
@DOB DateTime2
AS
--INSERT STATEMENT
SET @ID = SCOPE_IDENTITY()
POCO:
internal class Person
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime DOB { get; set; }
}
Код:
var procParams = new DynamicParameters(person);
connection.Execute("InsertPerson", procParams, commandType: CommandType.StoredProcedure);
// This is where i'm having the issue, can it be done?
person.ID = procParams.Get<int>("ID");
В настоящее время я получаю сообщение об ошибке, потому что ключ не был найден. Есть ли способ получить выходной параметр ID без ручной настройки всех сохраненных параметров процесса?
Ответ №1:
С помощью быстрой настройки, Add
теперь заменяет значение из шаблона, позволяя:
public void TestProcWithOutParameter()
{
connection.Execute(
@"CREATE PROCEDURE #TestProcWithOutParameter
@ID int output,
@Foo varchar(100),
@Bar int
AS
SET @ID = @Bar LEN(@Foo)");
var obj = new
{ // this could be a Person instance etc
ID = 0,
Foo = "abc",
Bar = 4
};
var args = new DynamicParameters(obj);
args.Add("ID", 0, direction: ParameterDirection.Output);
connection.Execute("#TestProcWithOutParameter", args,
commandType: CommandType.StoredProcedure);
args.Get<int>("ID").IsEqualTo(7);
}
Это достаточно близко? Вы также можете использовать ParameterDirection.ReturnValue
либо уже существующее значение, либо новое значение. Обратите внимание, что он не обновляется непосредственно обратно в исходный шаблон; значение должно быть извлечено из DynamicParameters
экземпляра (как показано).
Комментарии:
1. Я предполагаю, что значение может быть автоматически обновлено с помощью
.Output<T>(T, Expression<Func<T, object>>)
метода справаDynamicParameters
?
Ответ №2:
Когда вы используете конструктор для DynamicParameters
указания объекта шаблона, вам все равно нужно будет указать, что @ID
это выходной параметр. Сначала, через шаблон, ему будет присвоено значение ParameterDirection.Input
. Как только вы добавите его, он будет переопределен для получения обновленных значений, тогда вы можете получить значение по имени параметра следующим образом:
procParams.Add("@ID", dbType: DbType.Int32, direction: ParameterDirection.Output);
// ... execute ...
person.ID = procParams.Get<int>("@ID");
Я смог заставить это работать и использовал ваши классы и код, в дополнение к тому, что я показал выше.
РЕДАКТИРОВАТЬ: как обсуждалось в комментариях, хранимая процедура не принимает больше аргументов, чем она объявила. Таким образом, альтернативный подход заключается в том, чтобы отказаться от хранимой процедуры и прибегнуть к некоторому встроенному SQL. При использовании запроса Dapper будет игнорировать любые заданные ему параметры, которые не указаны в инструкции SQL. Это обходной путь для решения этой проблемы:
string sql = "INSERT INTO Person (Name, DOB) VALUES (@Name, @DOB) SELECT SCOPE_IDENTITY()";
decimal id = conn.Query<decimal>(sql, procParams).First();
person.ID = (int)id;
Обратите внимание, что SCOPE_IDENTITY()
возвращает десятичное число, а не int.
Другая идея, которая, на мой взгляд, не идеальна, заключается в изменении кода Dapper и добавлении Remove
метода в DynamicParameters
класс для удаления нежелательных параметров. Однако это не сильно сэкономит вам время, поскольку вы все равно потратите время на указание того, какие параметры следует удалить, чтобы сделать хранимую процедуру счастливой. Если вы решите реализовать это, помните, что регистр имеет значение при указании ключа для удаления из parameters
словаря.
Комментарии:
1. Я уже пробовал это, но получаю исключение «У процедуры или функции InsertPerson указано слишком много аргументов». Извините, следовало указать это в вопросе.
2. @Fermin исключение, которое вы получаете, возникает, когда параметры содержат больше аргументов, чем количество аргументов, объявленных в хранимой процедуре. Поскольку вы передаете
person
в качестве шаблонаDynamicParameters
конструктору, все свойства вPerson
классе имеют параметр, сгенерированный для него. Итак, я подозреваю, что у вас есть больше свойств, чем простоID
,Name
иDOB
в вашем классе. Если это так, то я думаю, вам нужно будет указать параметры самостоятельно, добавив их, как я показал выше.3. Я подозревал, что я пытаюсь написать общий класс DataAccess и поэтому пытался избежать указания параметров по отдельности.
4. @Ahmed спасибо за ваши мысли по этому поводу; так получилось, что
Add
это уже замена , поэтому во многих отношениях это уже должно было сработать, однако шаблон обрабатывается по-разному с явными параметрами по разным причинам. С помощью быстрой настройкиAdd
теперь можно переопределять значения из шаблона — смотрите Мой ответ о том, как это теперь может работать. Это не представляет риска для совместимости, поскольку ранее этот сценарий всегда приводил к сбою в виде исключения.5. @AhmadMageed Я перенес 1.7 в nuget