直接拼接字符串会导致sql注入,因用户输入被当sql代码执行;参数化查询通过@占位符和parameters.add()严格区分代码与数据,需注意命名一致、禁用拼接、null用dbnull.value等细节。

为什么直接拼接字符串会出问题
把用户输入直接塞进 SQL 语句里,比如 string sql = "SELECT * FROM Users WHERE Name = '" + userName + "'";,等于把数据库的钥匙交到用户手上。只要传入 ' OR '1'='1 这类内容,整个 WHERE 条件就失效了,可能查出所有用户,甚至执行 ); DROP TABLE Users; -- 这种破坏性语句。
参数化查询不是“过滤输入”,而是让数据库明确区分“代码”和“数据”——SQL 语句结构在编译时就固定,参数只作为值传入,不会被当作 SQL 语法解析。
SqlCommand.Parameters.Add() 的正确用法
.NET 中最常用、也最容易写错的是 Parameters.Add() 系列方法。关键点不是“加不加参数”,而是“怎么加”。
- 必须用
@paramName占位符(SQL Server)或?(SQLite/MySQL),且名称要和Add()传入的参数名严格一致 - 不要手动拼接引号、转义单引号,参数值原样传入即可
- 类型尽量显式指定,避免隐式转换引发性能或精度问题(比如用
SqlDbType.VarChar而非SqlDbType.String) - 示例:
cmd.CommandText = "SELECT * FROM Products WHERE Category = @cat AND Price
别踩这些坑:常见错误写法
- 错误:用
string.Format 或 $"" 拼接参数名,如 WHERE Id = {@id} —— 这仍是字符串拼接,@id 不会被识别为参数
- 错误:重复添加同名参数(尤其在循环中没清空
Parameters),导致 InvalidOperationException: The parameter '@p' was supplied multiple times
- 错误:把参数名写成
@@name 或 @name:,SQL Server 只认单个 @ 开头的标识符
- 错误:对
NULL 值直接赋 null,应改用 DBNull.Value,否则会抛 InvalidCastException
使用 SqlCommand.Prepare() 是否有必要?
string.Format 或 $"" 拼接参数名,如 WHERE Id = {@id} —— 这仍是字符串拼接,@id 不会被识别为参数 Parameters),导致 InvalidOperationException: The parameter '@p' was supplied multiple times @@name 或 @name:,SQL Server 只认单个 @ 开头的标识符 NULL 值直接赋 null,应改用 DBNull.Value,否则会抛 InvalidCastException
Prepare() 对高频、固定结构的查询(比如登录验证、订单查询)有收益,它让 SQL Server 预编译执行计划并缓存。但要注意:
- 必须在设置完所有参数后调用,否则抛
InvalidOperationException - 参数类型必须已明确(不能靠
.AddWithValue()推断),否则准备失败 - 实际项目中多数场景不需要显式调用——现代 ADO.NET 和 SQL Server 的自动参数化已足够可靠
- 过度使用反而增加首次开销,且对动态列名、表名无效(那些仍需白名单校验,无法参数化)
参数化本身不难,难的是坚持每一条 SQL 都走这条路;最容易被绕过的,往往是日志记录、动态排序字段、IN 列表这种“看起来不像注入点”的地方。











