【发布时间】:2016-05-03 14:30:52
【问题描述】:
在实现表值参数时,生成IEnumerable<SqlDataRecord> 以供参数使用的最常见方法之一是这样的代码(例如,https://stackoverflow.com/a/10779567/18192):
public static IEnumerable<SqlDataRecord> Rows(List<int> simpletable)
{
var smd = new []{ new SqlMetaData("id", SqlDbType.Int)};
var sqlRow = new SqlDataRecord(smd);
foreach (int i in simpletable)
{
sqlRow.SetInt32(0, i);
yield return sqlRow;
}
}
//...
var param = sqlCmd.Parameters.AddWithValue("@retailerIDs", Rows(mydata));
param.SqlDbType = SqlDbType.Structured;
param.TypeName = "myTypeName";
此代码似乎确实有效。虽然重用SqlMetaData 不会引起太多警钟,但在foreach 循环之外声明SqlDataRecord 对我来说非常可疑:
一个可变对象被修改然后重复生成。
作为一个令人担忧的例子,在 LinqPad 中调用 var x = Rows(new[] { 100, 200}.ToList()).ToList().Dump() 会输出 200,200。这种方法似乎依赖于实现细节(行被单独处理),但我没有看到任何承诺这一点的文档。
是否有一些缓解因素使这种方法安全?
【问题讨论】:
-
乍一看确实很可疑。 SqlDataRecord 继承自 System.Object,所以它是一个引用类型。如果这是一个普通的 foreach 循环,您将重复返回同一个对象。但是我认为 yield 关键字改变了一些事情。为每个项目调用一次方法 Rows,因此为每一行实例化一个新的 SqlDataRecord。请参阅 MSDN > yield(C# 参考):msdn.microsoft.com/en-us/library/9k7k7cf0.aspx
-
@RichardCL:SqlDataRecord 是一个引用类型。因此,
yield return正在吐出引用的副本,但该引用每次都指向同一个对象。因此,为什么我的示例 sn-p (var x = Rows(new[] { 100, 200}.ToList()).ToList().Dump()) 会吐出200,200而不是100,200。代码在SqlCommand的情况下有效的唯一原因是每一行都是单独处理的......但我相信这是一个实现细节,而不是记录的内容。 -
没有办法让这段代码安全,除非它只在一个线程上运行,并且返回值从不在它的枚举器之外使用- 从不转换为列表、数组或其他任何东西。这将使它无用,例如批处理操作,在网格上显示等。
-
@PanagiotisKanavos:正如我所说,这段代码被用作表值 SP 参数,所以这些问题都不适用,除非它们被各种 @ 的实现细节所违反987654336@ 来电。
-
@Brian 你应该用相反的方式表达这个:这段代码可以工作的唯一方法是如果
Execute以 非常 特定的方式编码并且没有其他代码结果在迭代器的枚举中,例如没有ToList或ToArray调用。否则,您会将一组 X 引用传递给同一对象。这么说很明显这段代码是不安全的。
标签: c# sql-server table-valued-parameters