61

将参数传递给 SQLCommand 的最佳方法是什么?你可以做:

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

或者

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

或者

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

似乎第一个在性能方面或在错误检查方面可能以某种方式“更好”。但我想更确切地知道。

4

5 回答 5

67

里面发生了什么?

您引用了几个重载的参数列表Add。这些是直接对应于SqlParameter类的构造函数重载的便利方法。它们本质上使用与您调用的便捷方法具有相同签名的任何构造函数来构造参数对象,然后SqlParameterCollection.Add(SqlParameter)像这样调用:

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

AddWithValue类似,但更方便,还设置了值。但是,它实际上是为了解决框架缺陷而引入的。引用 MSDN,

它的重载Add采用字符串和对象已被弃用,因为可能与 SqlParameterCollection.Add采用 aStringSqlDbType 枚举值的重载存在歧义,其中将整数与字符串一起传递可能被解释为参数值或相应的 SqlDbType值。AddWithValue 每当您想通过指定其名称和值来添加参数时使用。

类的构造函数重载SqlParameter仅仅是设置实例属性的便利。它们缩短了代码,对性能的影响很小:构造函数可以绕过 setter 方法并直接对私有成员进行操作。如果有区别,那也不会太大。

我该怎么办?

请注意以下内容(来自 MSDN)

对于双向和输出参数以及返回值,您必须设置 的值Size。这对于输入参数不是必需的,如果没有显式设置,则在执行参数化语句时从指定参数的实际大小推断该值。

默认类型是输入。但是,如果您允许像这样推断大小并且在循环中回收参数对象(您确实说过您关心性能),那么大小将由第一个值设置,任何后续值都将是剪辑。显然,这仅对可变​​长度值(例如字符串)有意义。

如果您在循环中重复传递相同的逻辑参数,我建议您在循环外创建一个 SqlParameter 对象并适当调整其大小。过大的 varchar 是无害的,所以如果它是一个 PITA 以获得确切的最大值,只需将其设置为比您期望的列更大。因为您正在回收对象而不是为每次迭代创建一个新对象,所以即使您对超大尺寸感到有点兴奋,循环期间的内存消耗也可能会下降。

说实话,除非您处理数千个电话,否则这些都不会产生太大影响。AddWithValue创建一个新对象,回避大小问题。它简短而甜美,易于理解。如果您遍历数千个,请使用我的方法。如果您不这样做,请使用它AddWithValue来保持您的代码简单且易于维护。


2008 年是很久以前的事了

自从我写这篇文章以来的几年里,世界发生了变化。有新的日期类型,还有一个问题没有出现在我的脑海中,直到最近的一个日期问题让我思考扩大的含义。

对于那些不熟悉这些术语的人来说,扩大和缩小是数据类型转换的特性。如果将 int 分配给 double,则不会损失精度,因为 double 更“宽”。这样做总是安全的,因此转换是自动的。这就是为什么你可以将一个 int 分配给一个双精度,但反过来你必须进行显式转换 - 双精度到 int 是一种缩小转换,可能会损失精度。

这可以应用于字符串:NVARCHAR 比 VARCHAR 更宽,因此您可以将 VARCHAR 分配给 NVARCHAR,但反之则需要强制转换。比较有效,因为 VARCHAR 隐式扩展到 NVARCHAR,但这会干扰索引的使用!

C# 字符串是 Unicode,因此 AddWithValue 将产生一个 NVARCHAR 参数。在另一端,VARCHAR 列值扩大到 NVARCHAR 以进行比较。这不会停止查询执行,但会阻止使用索引。这是不好的。

你能为这个做什么?您有两种可能的解决方案。

  • 显式键入参数。这意味着不再有 AddWithValue
  • 将所有字符串列类型更改为 NVARCHAR。

放弃 VARCHAR 可能是最好的主意。这是一个具有可预测后果的简单更改,它改善了您的本地化故事。但是,您可能没有此选项。

这些天我没有做很多直接的 ADO.NET。Linq2Sql 现在是我的首选武器,编写此更新的行为让我想知道它是如何处理这个问题的。我突然渴望通过 VARCHAR 列检查我的数据访问代码以进行查找。


2019 年,世界再次前行

Linq2Sql 在 dotnet Core 中不可用,所以我发现自己在使用 Dapper。[N]VARCHAR 问题仍然存在,但它不再被埋没。我相信人们也可以使用 ADO,所以这方面的事情已经完成了一个完整的循环。

于 2008-11-17T06:00:01.017 回答
59

您也可以使用AddWithValue(),但要注意可能出现错误的隐式类型转换。

cmd.Parameters.AddWithValue("@Name", "Bob");
于 2008-11-16T00:59:41.170 回答
7

我曾经使用您的选项1:

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

效果很好,但后来我开始使用 .AddWithValue ,它变得非常简单。经过数千次使用后,它并没有给我带来任何问题。请注意,我几乎总是传递我的类私有变量,所以我不必担心隐式类型转换。

于 2008-11-16T03:30:00.537 回答
5

我肯定会说#1。但是,微软在企业库中的数据访问应用程序块中做到这一点是最好的,尤其是对于 SQL 服务器:

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

于 2008-11-16T01:17:57.043 回答
3

这取决于您的应用程序。我实际上喜欢 2,因为如果我更改存储的 proc 参数的长度,我不必更改我的 DAO。但这只是我。我不知道是否有任何性能惩罚或任何东西。

于 2008-11-17T04:41:28.443 回答