When implementing table-valued parameters, one of the most common ways to generate an IEnumerable<SqlDataRecord>
for use by the parameter is code like this (e.g., 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";
This code does seem to work. While reusing SqlMetaData
does not set off too many alarm bells, declaring the SqlDataRecord
outside the foreach
loop feels incredibly suspicious to me:
A mutable object is modified and then yielded repeatedly.
As an example of why this is concerning, calling var x = Rows(new[] { 100, 200}.ToList()).ToList().Dump()
in LinqPad spits out 200,200
. This approach seems to rely on an implementation detail (that rows are processed individually), but I don't see any documentation which promises this.
Is there some mitigating factor which renders this approach safe?