.NET应用中如何正确向SQL存储过程传递nvarchar参数?
问题描述
执行带参数的SQL存储过程时,传递名为office的nvarchar参数遇到困难。直接在MS SQL中执行存储过程时,给参数加双单引号才能生效,但在C#代码中同样添加双单引号后仍无法正常运行,报错:
Incorrect Syntax near 'Nord'.
即使去掉代码中的双单引号,或者传递不含空格的参数,错误依然存在。
相关代码
C#调用代码
parameters.Add("office", "''Niederlassung Nord''"); //<---问题所在 parameters.Add("year", "2023"); ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[sqlConn]; using (SqlConnection conn = new SqlConnection(settings.ConnectionString)) { using (SqlCommand storedCmd = new SqlCommand(sql, conn)) { conn.Open(); storedCmd.CommandType = CommandType.StoredProcedure; storedCmd.CommandTimeout = 1200; if (parameters != null) { foreach (KeyValuePair<string, object> value in parameters) { storedCmd.Parameters.AddWithValue("@" + value.Key, value.Value); } } using (SqlDataReader reader = storedCmd.ExecuteReader()) { while (reader.HasRows) { while (reader.Read()) { Dictionary<string, string> row = new Dictionary<string, string>(); for (var i = 0; i < reader.FieldCount; i++) // 单个字段 { row.Add(reader.GetName(i), reader[i].ToString());//getname用于获取列名 } currentList.Add(row); } } } } }
直接在SQL中执行的有效语句
USE [ServerName] GO DECLARE @return_value int EXEC @return_value = [dbo].[GetData] @office = N'''Niederlassung Nord''', //<-- 只有这样加双单引号才生效 @year= N'2023' SELECT 'Return Value' = @return_value GO
存储过程核心代码
USE [ServerName] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO ALTER PROCEDURE [dbo].[GetData] @office nvarchar(4000) = null, @year nvarchar(4) = null AS BEGIN CREATE TABLE #masterData2 ( [office] [nvarchar](40) NULL, ) ... if (@office is not null) begin if (@sql1 is not null) select @sql1 += ' AND #masterData2.[office ] in (' + @office + ')' else select @sql1 = ' #masterData2.[office ] in (' + @office + ')' end ...
解决方法
问题根源在于存储过程使用了动态SQL拼接,且未正确处理参数,直接将@office参数拼入SQL语句导致语法错误。以下是两种解决方式:
方法一:修改存储过程,使用参数化动态SQL(推荐)
彻底避免字符串拼接,改用参数化动态SQL,既解决语法问题,还能防范SQL注入:
ALTER PROCEDURE [dbo].[GetData] @office nvarchar(4000) = null, @year nvarchar(4) = null AS BEGIN CREATE TABLE #masterData2 ( [office] [nvarchar](40) NULL, ) ... DECLARE @sql1 NVARCHAR(MAX) = ''; -- 处理office参数 IF (@office IS NOT NULL) BEGIN IF (@sql1 <> '') SET @sql1 += ' AND #masterData2.[office] IN (@officeParam)' ELSE SET @sql1 += ' #masterData2.[office] IN (@officeParam)' -- 执行动态SQL时传入参数 EXEC sp_executesql @sql1, N'@officeParam NVARCHAR(4000)', @officeParam = @office END ...
同时,C#代码直接传递原始字符串即可,无需额外加单引号:
parameters.Add("office", "Niederlassung Nord"); // 直接传原始字符串
方法二:保留原存储过程逻辑(临时方案,不推荐)
若无法修改存储过程,需在C#中传递符合IN子句格式的字符串,注意正确转义单引号:
// 单个值时,传递带单引号的字符串(用双单引号转义) parameters.Add("office", "'Niederlassung Nord'"); // 多个值时,格式为"'值1','值2'",比如:"'Niederlassung Nord','Niederlassung Süd'"
此方式存在SQL注入风险,仅作临时应急使用。
内容的提问来源于stack exchange,提问作者user2018
相关产品推荐
相关产品推荐

