C# 运行 临时存储过程
C# running temporary stored procedure
我有一个 SQL 语句,我需要在 C# 中 运行 并且需要从 C# 代码中获取参数。我知道存储过程是避免 SQL 注入的首选,但我只是想在 C# 中执行此操作。
我正在将此 SQL 翻译成 C#,但我遇到了一个错误,即使查询在 SQL Server Management Studio 中有效。它使用临时存储过程和临时 table 下面:
-- 1.) Declare a criteria table which can be any number of rows
BEGIN TRY
DROP TABLE #CriteriaTable
END TRY
BEGIN CATCH
END CATCH
CREATE TABLE #CriteriaTable (ParameterCode VARCHAR(64), Value VARCHAR(64))
-- 2.) Declare a procedure to add criteria table
BEGIN TRY
DROP PROCEDURE #AddCriteriaTable
END TRY
BEGIN CATCH
END CATCH
go
CREATE PROCEDURE #AddCriteriaTable
(@ParameterCode VARCHAR(64), @Value VARCHAR(64))
AS
INSERT #CriteriaTable
VALUES(@ParameterCode, @Value)
GO
-- 3.) Do a computation which accesses the criteria
BEGIN TRY
DROP PROCEDURE #ComputeBasedOnCriteria
END TRY
BEGIN CATCH
END CATCH
go
CREATE PROCEDURE #ComputeBasedOnCriteria
(@product VARCHAR(36) = 'ABC',
@currency VARCHAR(3) = 'USD',
@zScore FLOAT = .845)
AS
-- Code inside this procedure is largely dynamic sql.
-- This is just a quick mock up
SELECT
@Product ProductCode,
@currency Currency,
950 ExpectedRevenue,
*
FROM
#CriteriaTable c
PIVOT
(min (Value) FOR ParameterCode IN
([MyParam1], MyParam2, MyParam3)
) AS pvt
GO
--End of code for Configuration table
-- Samples: Execute this to add criteria to the temporary table that will be used by #ComputeBasedOnCriteria
EXEC #AddCriteriaTable 'MyParam1', 'MyValue1'
EXEC #AddCriteriaTable 'MyParam2', 'MyValue3'
EXEC #AddCriteriaTable 'MyParam3', 'MyValue3'
--Execute the procedure that will return the results for the screen
EXEC #ComputeBasedOnCriteria
现在在 C# 中尝试此操作时,我在尝试 运行 #AddCriteriaTable
过程时遇到错误。当我尝试 运行 倒数第二行的 ExecuteQuery
时,它会抛出:
Exception: System.Data.SqlClient.SqlException, Incorrect syntax near the keyword 'PROC'.
为什么它在 SQL 服务器中有效,但在 C# 代码中无效?在 C# 中还有其他方法可以做到这一点吗?让我知道是否有我应该遵循的 C# 指南,因为我仍在学习这个 C# - 数据库工作。
编辑:
我知道我可以把它作为一个普通的存储过程并传入一个数据表,但是有一些团队问题我不能说,它迫使我将 sp 用作文本。
失败的原因是您将参数传递给此处的 CREATE PROC
部分:
cmd.CommandText = @"CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)";
cmd.Parameters.AddWithValue("@ParameterCode", request.Criteria.First().Key;
cmd.Parameters.AddWithValue("@Value", request.Criteria.First().Value;
var reader2 = cmd.ExecuteReader();
在这里传递值没有意义,因为你只是创建过程,你只需要在执行过程时传递它们。如果你 运行 跟踪你会看到在服务器上执行的是这样的东西:
EXEC sp_executesql
N'CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)',
N'@ParameterCode VARCHAR(64),@Value VARCHAR(64)',
@ParameterCode = 'MyParam1',
@Value = 'MyValue1'
在 SSMS 中 运行 时会抛出相同的错误语法错误。您只需要:
EXEC sp_executesql
N'CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)';
所以在 c# 中你需要:
//First Create the procedure
cmd.CommandText = @"CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)";
cmd.ExecuteNoneQuery();
//Update the command text to execute it, then add parameters
cmd.CommandText = "EXECUTE #AddCriteriaTable @ParameterCode, @Value;";
cmd.Parameters.AddWithValue("@ParameterCode", request.Criteria.First().Key;
cmd.Parameters.AddWithValue("@Value", request.Criteria.First().Value;
var reader2 = cmd.ExecuteReader();
我认为你把一切都复杂化了,一个将数据添加到临时 table 的临时存储过程似乎太过分了。
如果您是从代码执行的,那么您可能需要重用所有内容,那么为什么不为您的计算设置一个永久性过程,
然后使用定义的类型来管理执行实例。
所以首先创建你的类型:
CREATE TYPE dbo.CriteriaTableType AS TABLE (ParameterCode VARCHAR(64), Value VARCHAR(64));
然后创建您的程序:
CREATE PROC dbo.ComputeBasedOnCriteria
(
@product VARCHAR(36)='ABC',
@currency VARCHAR(3)='USD',
@zScore FLOAT = .845,
@CriteriaTable dbo.CriteriaTableType READONLY
)
AS
--Code inside this proc is largely dynamic sql. This is just a quick mock up
SELECT
@Product ProductCode
,@currency Currency
,950 ExpectedRevenue
,*
FROM @CriteriaTable c
PIVOT (MIN (Value) FOR ParameterCode IN (MyParam1, MyParam2,MyParam3)) AS pvt;
GO
然后最后到运行:
DECLARE @Criteria dbo.CriteriaTableType;
INSERT @Criteria
VALUES
('MyParam1', 'MyValue1'),
('MyParam2', 'MyValue2'),
('MyParam3', 'MyValue3');
EXECUTE dbo.ComputeBasedOnCriteria @CriteriaTable = @Criteria;
您甚至可以在 c# 中填充条件 table,然后将其从 c# 传递给过程。
var table = new DataTable();
table.Columns.Add("ParameterCode", typeof(string)).MaxLength = 64;
table.Columns.Add("Value", typeof(string)).MaxLength = 64;
foreach (var criterion in request.Criteria)
{
var newRow = table.NewRow();
newRow[0] = criterion.Key;
newRow[1] = criterion.Value;
table.Rows.Add(newRow);
}
using (var connection = new SqlConnection("connectionString"))
using (var command = new SqlCommand("dbo.ComputeBasedOnCriteria", connection))
{
var tvp = command.Parameters.Add("@CriteriaTable", SqlDbType.Structured);
tvp.TypeName = "dbo.CriteriaTableType";
tvp.Value = table;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
//Do Something with your results
}
}
}
如果您正在执行 SQL 以通过 C# 创建存储过程,那么您最好通过 C# 执行 SQL 而忘记过程。
使用存储过程来避免 SQL 注入的要点仅适用于存储过程已存在于服务器上并且您不是通过代码创建它的情况。
您可以通过使用参数化查询避免 SQL 注入。
参数通过验证数据类型来防止 sql 注入。因此,如果您在代码中插入一个整数,那么尝试注入的人将无法提供包含特殊字符的字符串,这会改变您的预期结果。
但是除此之外,你会收到一个错误,因为你在 C# 中的 SQL 中有 CREATE PROC
而不是 CREATE PROCEDURE
我有一个 SQL 语句,我需要在 C# 中 运行 并且需要从 C# 代码中获取参数。我知道存储过程是避免 SQL 注入的首选,但我只是想在 C# 中执行此操作。
我正在将此 SQL 翻译成 C#,但我遇到了一个错误,即使查询在 SQL Server Management Studio 中有效。它使用临时存储过程和临时 table 下面:
-- 1.) Declare a criteria table which can be any number of rows
BEGIN TRY
DROP TABLE #CriteriaTable
END TRY
BEGIN CATCH
END CATCH
CREATE TABLE #CriteriaTable (ParameterCode VARCHAR(64), Value VARCHAR(64))
-- 2.) Declare a procedure to add criteria table
BEGIN TRY
DROP PROCEDURE #AddCriteriaTable
END TRY
BEGIN CATCH
END CATCH
go
CREATE PROCEDURE #AddCriteriaTable
(@ParameterCode VARCHAR(64), @Value VARCHAR(64))
AS
INSERT #CriteriaTable
VALUES(@ParameterCode, @Value)
GO
-- 3.) Do a computation which accesses the criteria
BEGIN TRY
DROP PROCEDURE #ComputeBasedOnCriteria
END TRY
BEGIN CATCH
END CATCH
go
CREATE PROCEDURE #ComputeBasedOnCriteria
(@product VARCHAR(36) = 'ABC',
@currency VARCHAR(3) = 'USD',
@zScore FLOAT = .845)
AS
-- Code inside this procedure is largely dynamic sql.
-- This is just a quick mock up
SELECT
@Product ProductCode,
@currency Currency,
950 ExpectedRevenue,
*
FROM
#CriteriaTable c
PIVOT
(min (Value) FOR ParameterCode IN
([MyParam1], MyParam2, MyParam3)
) AS pvt
GO
--End of code for Configuration table
-- Samples: Execute this to add criteria to the temporary table that will be used by #ComputeBasedOnCriteria
EXEC #AddCriteriaTable 'MyParam1', 'MyValue1'
EXEC #AddCriteriaTable 'MyParam2', 'MyValue3'
EXEC #AddCriteriaTable 'MyParam3', 'MyValue3'
--Execute the procedure that will return the results for the screen
EXEC #ComputeBasedOnCriteria
现在在 C# 中尝试此操作时,我在尝试 运行 #AddCriteriaTable
过程时遇到错误。当我尝试 运行 倒数第二行的 ExecuteQuery
时,它会抛出:
Exception: System.Data.SqlClient.SqlException, Incorrect syntax near the keyword 'PROC'.
为什么它在 SQL 服务器中有效,但在 C# 代码中无效?在 C# 中还有其他方法可以做到这一点吗?让我知道是否有我应该遵循的 C# 指南,因为我仍在学习这个 C# - 数据库工作。
编辑: 我知道我可以把它作为一个普通的存储过程并传入一个数据表,但是有一些团队问题我不能说,它迫使我将 sp 用作文本。
失败的原因是您将参数传递给此处的 CREATE PROC
部分:
cmd.CommandText = @"CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)";
cmd.Parameters.AddWithValue("@ParameterCode", request.Criteria.First().Key;
cmd.Parameters.AddWithValue("@Value", request.Criteria.First().Value;
var reader2 = cmd.ExecuteReader();
在这里传递值没有意义,因为你只是创建过程,你只需要在执行过程时传递它们。如果你 运行 跟踪你会看到在服务器上执行的是这样的东西:
EXEC sp_executesql
N'CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)',
N'@ParameterCode VARCHAR(64),@Value VARCHAR(64)',
@ParameterCode = 'MyParam1',
@Value = 'MyValue1'
在 SSMS 中 运行 时会抛出相同的错误语法错误。您只需要:
EXEC sp_executesql
N'CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)';
所以在 c# 中你需要:
//First Create the procedure
cmd.CommandText = @"CREATE PROC #AddCriteriaTable (@ParameterCode VARCHAR(64), @Value VARCHAR(64)) AS INSERT #CriteriaTable VALUES (@ParameterCode, @Value)";
cmd.ExecuteNoneQuery();
//Update the command text to execute it, then add parameters
cmd.CommandText = "EXECUTE #AddCriteriaTable @ParameterCode, @Value;";
cmd.Parameters.AddWithValue("@ParameterCode", request.Criteria.First().Key;
cmd.Parameters.AddWithValue("@Value", request.Criteria.First().Value;
var reader2 = cmd.ExecuteReader();
我认为你把一切都复杂化了,一个将数据添加到临时 table 的临时存储过程似乎太过分了。 如果您是从代码执行的,那么您可能需要重用所有内容,那么为什么不为您的计算设置一个永久性过程, 然后使用定义的类型来管理执行实例。
所以首先创建你的类型:
CREATE TYPE dbo.CriteriaTableType AS TABLE (ParameterCode VARCHAR(64), Value VARCHAR(64));
然后创建您的程序:
CREATE PROC dbo.ComputeBasedOnCriteria
(
@product VARCHAR(36)='ABC',
@currency VARCHAR(3)='USD',
@zScore FLOAT = .845,
@CriteriaTable dbo.CriteriaTableType READONLY
)
AS
--Code inside this proc is largely dynamic sql. This is just a quick mock up
SELECT
@Product ProductCode
,@currency Currency
,950 ExpectedRevenue
,*
FROM @CriteriaTable c
PIVOT (MIN (Value) FOR ParameterCode IN (MyParam1, MyParam2,MyParam3)) AS pvt;
GO
然后最后到运行:
DECLARE @Criteria dbo.CriteriaTableType;
INSERT @Criteria
VALUES
('MyParam1', 'MyValue1'),
('MyParam2', 'MyValue2'),
('MyParam3', 'MyValue3');
EXECUTE dbo.ComputeBasedOnCriteria @CriteriaTable = @Criteria;
您甚至可以在 c# 中填充条件 table,然后将其从 c# 传递给过程。
var table = new DataTable();
table.Columns.Add("ParameterCode", typeof(string)).MaxLength = 64;
table.Columns.Add("Value", typeof(string)).MaxLength = 64;
foreach (var criterion in request.Criteria)
{
var newRow = table.NewRow();
newRow[0] = criterion.Key;
newRow[1] = criterion.Value;
table.Rows.Add(newRow);
}
using (var connection = new SqlConnection("connectionString"))
using (var command = new SqlCommand("dbo.ComputeBasedOnCriteria", connection))
{
var tvp = command.Parameters.Add("@CriteriaTable", SqlDbType.Structured);
tvp.TypeName = "dbo.CriteriaTableType";
tvp.Value = table;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
//Do Something with your results
}
}
}
如果您正在执行 SQL 以通过 C# 创建存储过程,那么您最好通过 C# 执行 SQL 而忘记过程。
使用存储过程来避免 SQL 注入的要点仅适用于存储过程已存在于服务器上并且您不是通过代码创建它的情况。
您可以通过使用参数化查询避免 SQL 注入。 参数通过验证数据类型来防止 sql 注入。因此,如果您在代码中插入一个整数,那么尝试注入的人将无法提供包含特殊字符的字符串,这会改变您的预期结果。
但是除此之外,你会收到一个错误,因为你在 C# 中的 SQL 中有 CREATE PROC
而不是 CREATE PROCEDURE