Могу ли я использовать DynamicParameters с шаблоном и иметь возвращаемый параметр в dapper?

#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