SqlParameter必须通过SqlCommand.Parameters.Add()添加才生效,单独创建无效;占位符名须严格一致,先设CommandText再加参数;禁用AddWithValue,显式指定SqlDbType和长度;表名、列名等SQL结构不可参数化,须白名单或QUOTENAME校验;存储过程调用需匹配ParameterDirection且显式命名。

SqlParameter必须配合SqlCommand.Parameters.Add()才生效
单独创建SqlParameter对象但不加进cmd.Parameters集合,等于没用——SQL Server根本收不到这个参数。常见错误是写了new SqlParameter("@id", 123)却忘了cmd.Parameters.Add(),结果查询始终返回空,还以为是逻辑问题,其实是参数根本没传过去。
正确做法只有这一种路径:cmd.CommandText里写带@xxx占位符的SQL,再用cmd.Parameters.Add()或AddWithValue()把值塞进去。两者顺序不能颠倒:先设CommandText,再加参数。
- 占位符名必须和
Add()里的名字严格一致,大小写敏感(SQL Server默认不区分,但.NET参数匹配区分) - 如果SQL里写的是
@user_id,代码里却Add("@userid", ...),查询不会报错,但条件恒假 - 执行前建议调用
cmd.Parameters.Clear(),尤其在复用SqlCommand实例时,避免旧参数干扰
别用AddWithValue,显式指定SqlDbType
AddWithValue看着省事,实际是隐式类型陷阱。它根据C#值推断SQL类型:传"123" → VarChar(3),但存储过程定义的是INT,SQL Server就得做隐式转换——轻则索引失效,重则转换失败报错。
更危险的是空字符串"":推成VARCHAR(0),而字段是NVARCHAR(50),可能触发截断警告;枚举值直接传,会被当成INT,但存储过程参数是TINYINT,越界就崩。
- 用
cmd.Parameters.Add("@name", SqlDbType.NVarChar, 50).Value = name明确长度和类型 - 传
null时,必须写DBNull.Value,不能写null或留空 - 数值类参数优先用
SqlDbType.Int、SqlDbType.BigInt,别依赖字符串推断
表名、列名、排序字段不能参数化,得另想办法
SqlParameter只对“值”有效,对SQL结构无效。下面这行代码依然高危:"SELECT * FROM " + tableName + " WHERE id = @id"——tableName是用户输入,拼进去就是注入入口。
Triggers when a user provides a training-area video of a pet for analysis; supports local uploads or network URLs to call server-side APIs for command-execution recognition, detecting whether the pet's body posture matches the issued commands (Sit / Down
这类动态对象名必须白名单校验或用QUOTENAME()包裹。例如在存储过程中拼接表名,得写DECLARE @sql NVARCHAR(MAX) = 'SELECT * FROM ' + QUOTENAME(@tableName) + ' WHERE id = @id',再用sp_executesql执行,并把@id作为参数传入。
- C#层传
tableName进来时,先查白名单字典:if (!validTables.Contains(tableName)) throw new ArgumentException(); - 绝对不要用
Replace("'", "''")或正则删单引号——绕过手段太多,不解决根本问题 - ORDER BY字段同理,
ORDER BY @sortColumn语法非法,必须走白名单或QUOTENAME
存储过程调用时参数顺序和方向不能错
调用存储过程时,SqlParameter的ParameterDirection必须匹配定义。比如存储过程有@retCode INT OUTPUT,C#里就得写:var p = cmd.Parameters.Add("@retCode", SqlDbType.Int); p.Direction = ParameterDirection.Output;,漏设Direction会导致输出参数始终为0或null。
还有种隐形坑:存储过程定义参数顺序是@a INT, @b NVARCHAR(50),但C#里先Add("@b", ...)再Add("@a", ...),又没写参数名(靠位置匹配),那值就全传反了。
- 永远显式写参数名,别依赖顺序;SQL Server支持命名绑定,不看位置
- 输出参数、返回值参数必须设
Direction,且读取要在ExecuteNonQuery()之后 - 存储过程内静态SQL(如
WHERE id = @id)天然防注入,但内部若拼接SQL,仍要按前述规则处理
真正卡住人的点往往不在“会不会用”,而在“以为用了其实没生效”——比如参数名拼错、类型隐式转换、对象名硬拼、输出参数没设方向。这些地方一漏,前面所有参数化都归零。

















