Какой лучший способ передачи параметров в SQLCommand? - PullRequest
51 голосов
/ 16 ноября 2008

Какой лучший способ передачи параметров в SQLCommand? Вы можете сделать:

cmd.Parameters.Add("@Name", SqlDbType.VarChar, 20).Value = "Bob";

или

cmd.Parameters.Add("@Name", SqlDbType.VarChar).Value = "Bob";

или

cmd.Parameters.Add("@Name").Value = "Bob";

Похоже, что первый может быть как-то "лучше", либо с точки зрения производительности, либо с точки зрения проверки ошибок. Но я хотел бы знать более определенно.

Ответы [ 5 ]

59 голосов
/ 17 ноября 2008

Что там происходит?

Вы указываете списки параметров для нескольких перегрузок Add. Это вспомогательные методы, которые напрямую соответствуют перегрузкам конструктора для класса SqlParameter. Они по существу конструируют объект параметра, используя любой конструктор, имеющий ту же сигнатуру, что и вызываемый вами вспомогательный метод, а затем вызывают SqlParameterCollection.Add(SqlParameter) следующим образом:

SqlParameter foo = new SqlParameter(parameterName, dbType, size);
this.Add(foo);

AddWithValue аналогичен, но еще более удобен, также устанавливает значение. Однако, это было фактически введено, чтобы устранить недостаток структуры. Цитирую MSDN,

Перегрузка Add, которая принимает строка и объект устарели из-за возможной неопределенности с SqlParameterCollection.Add перегрузка это занимает String и SqlDbType значение перечисления, где передача целое число со строкой может быть интерпретируется как либо значение параметра или соответствующий SqlDbType значение. Используйте AddWithValue всякий раз, когда вы хотите добавить параметр указав его имя и значение.

Перегрузки конструктора для класса SqlParameter - это просто удобство для установки свойств экземпляра. Они сокращают код, оказывая незначительное влияние на производительность: конструктор может обходить методы установки и работать непосредственно с закрытыми членами. Если будет какая-то разница, ее будет не много.

Что мне делать?

Обратите внимание на следующее (из MSDN)

Для двунаправленного и выходного параметры и возвращаемые значения, вы должен установить значение Size. Это не требуется для входных параметров, и если явно не установлено, значение выводится из фактического размера указанный параметр, когда выполняется параметризованный оператор.

Тип по умолчанию - ввод. Тем не менее, если вы позволите выводить размер таким образом и перезапустите объект параметра в цикле (вы уже говорили, что вас беспокоит производительность), тогда размер будет установлен по первому значению, а все последующие значения, которые будут длиннее, будут обрезается. Очевидно, что это важно только для значений переменной длины, таких как строки.

Если вы передаете один и тот же логический параметр в цикле несколько раз, я рекомендую вам создать объект SqlParameter вне цикла и соответствующим образом изменить его размер. Чрезмерный размер varchar безвреден, поэтому, если это точный максимум PITA, просто установите его больше, чем вы когда-либо ожидали. Поскольку вы перерабатываете объект, а не создаете новый для каждой итерации, потребление памяти в течение цикла может, вероятно, упасть , даже если вы немного взволнованы из-за увеличения.

По правде говоря, если вы не обработаете тысячи вызовов, ни одно из этого не будет иметь большого значения. AddWithValue создает новый объект, обходя проблему с размерами. Это коротко и сладко и легко понять. Если вы перебираете тысячи, используйте мой подход. Если вы этого не сделаете, используйте AddWithValue, чтобы сохранить ваш код простым и легким в обслуживании.


2008 год был давно

За те годы, когда я написал это, мир изменился. Есть новые виды дат, и есть проблема, которая не приходила мне в голову, пока недавняя проблема с датами не заставила меня задуматься о последствиях расширения.

Расширение и сужение для тех, кто не знаком с терминами, являются качествами преобразования типов данных. Если вы присваиваете int двойному типу, нет потери точности, потому что double "шире". Это всегда безопасно, поэтому конвертация происходит автоматически. Вот почему вы можете присвоить int двойному типу, но в другом случае вы должны выполнить явное приведение - double к int - это сужающее преобразование с потенциальной потерей точности.

Это может применяться к строкам: NVARCHAR шире, чем VARCHAR, поэтому вы можете назначить VARCHAR для NVARCHAR, но для перехода в другой путь требуется приведение. Сравнение работает, потому что VARCHAR неявно расширяется до NVARCHAR, , но это будет мешать использованию индексов!

Строки C # имеют Unicode, поэтому AddWithValue создаст параметр NVARCHAR. На другом конце значения столбцов VARCHAR расширяются до NVARCHAR для сравнения. Это не останавливает выполнение запроса, но предотвращает использование индексов. Это плохо.

Что вы можете с этим поделать? У вас есть два возможных решения.

  • Явно введите параметр. Это означает, что больше нет AddWithValue
  • Измените все типы строковых столбцов на NVARCHAR.

Погружение VARCHAR, вероятно, лучшая идея. Это простое изменение с предсказуемыми последствиями, которое улучшает вашу историю локализации. Тем не менее, вы можете не иметь это в качестве опции.

В наши дни я не слишком много занимаюсь прямым ADO.NET. Linq2Sql теперь мое любимое оружие, и сам процесс написания этого обновления заставил меня задуматься над тем, как он справляется с этой проблемой. У меня внезапно возникает жгучее желание очистить мои базы данных VARCHAR.

49 голосов
/ 16 ноября 2008

Вы также можете использовать AddWithValue(), но помните о возможности неправильного неявного преобразования типов.

cmd.Parameters.AddWithValue("@Name", "Bob");
4 голосов
/ 16 ноября 2008

Я бы сказал # 1 наверняка. Но, тем не менее, Microsoft делает это в блоке приложений для доступа к данным в корпоративной библиотеке, лучше всего, особенно для SQL-сервера:

http://msdn.microsoft.com/en-us/library/dd203144.aspx

3 голосов
/ 16 ноября 2008

Я использовал ваш вариант 1:

cmd.Parameters.Add ("@ Name", SqlDbType.VarChar, 20) .Value = "Bob";

, который работал нормально, но потом я начал использовать .AddWithValue, и это так просто, как только можно. Это не вызвало у меня проблемы после многих тысяч применений. Имейте в виду, я почти всегда передаю приватные переменные своих классов, поэтому мне не нужно беспокоиться о неявном преобразовании типов.

2 голосов
/ 17 ноября 2008

Это зависит от вашего приложения. Мне действительно нравится 2, потому что мне не нужно менять DAO, если я изменяю длину хранимого параметра proc. Это только я, хотя. Я не знаю, есть ли какие-либо потери производительности или что-то в этом роде.

...