【问题标题】:C# running temporary stored procedureC#运行临时存储过程
【发布时间】:2016-08-02 16:35:51
【问题描述】:

我有一个需要在 C# 中运行的 SQL 语句,并且需要从 C# 代码中获取参数。我知道存储过程是避免 SQL 注入的首选,但我只是想在 C# 中执行此操作。

我正在将此 SQL 转换为 C#,但即使查询在 SQL Server Management Studio 中运行,我也遇到了错误。它使用下面的临时存储过程和临时表:

-- 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 时,它会抛出:

异常:System.Data.SqlClient.SqlException,关键字“PROC”附近的语法不正确。

为什么它在 SQL Server 中有效,而在 C# 代码中无效?在 C# 中还有另一种方法可以做到这一点吗?让我知道是否有我应该遵循的 c# 指南,因为我仍在学习这个 c# - db 工作。

编辑: 我知道我可以将其作为普通存储过程执行并传入 DataTable,但是我不能说团队问题,它迫使我将 sp 用作文本。

【问题讨论】:

  • 我猜它更多的是你如何做 c# 调用这就是问题所在。最后一个 exec #computebasedoncritieria 之前的所有行都是 executenonquery,行,最后一个需要是普通查询才能获取数据 - 你能显示你的 c# 代码吗
  • 我粘贴了包含我的 c# 代码的图像。我还没有编写#computebasedoncritieria;执行 #AddCriteriaTable 会引发错误。
  • 眯着眼睛看你的代码我不希望像你一样制作这个过程,因为你似乎在制作它时调用它,我希望你用另一个 execnoquery 制作它,然后运行它..在最后一组命令中似乎混合了两者

标签: c# sql-server tsql sqlcommand


【解决方案1】:

失败的原因是您在此处将参数传递给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();

我认为您将一切都复杂化了,将数据添加到临时表的临时存储过程似乎过分了。 如果您从代码执行,您似乎需要重用所有内容,那么为什么不为您的计算提供一个永久程序, 然后使用定义的类型来管理执行的实例。

所以首先创建你的类型:

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# 中填充标准表,然后将其从 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
            }
        }
    }

【讨论】:

  • 其实这个词是'PROC'。 SO中的某人将其编辑为“程序”。我现在把它退回了。屏幕截图中的错误基于 ExecuteReader 执行的第三个命令文本。我仍然不清楚为什么会失败。我会检查你的代码,谢谢!
  • 我非常感谢您为显示代码所做的辛勤工作。只是我此时无法使用 SP 方法(请参阅编辑)。我最初将它设计为与您建议的类似,但最终不得不成为文本 sp。临时表是为了让两个 SP 可以使用数据..
  • 我已经弄清楚了,我在编辑中解释得更好,但要点是您将参数值传递给 CREATE PROC 语句,这不是必需的。
【解决方案2】:

如果您正在执行 SQL 以通过 C# 创建存储过程,那么您不妨只通过 C# 执行 SQL 而忘记过程。

使用存储过程来避免 SQL 注入的要点仅适用于存储过程已经存在于服务器上并且您没有通过代码创建它的情况。

您可以通过使用参数化查询来避免 SQL 注入。 参数通过验证数据类型来防止 sql 注入。因此,如果您在代码中插入一个整数,那么尝试注入的人将无法提供带有特殊字符的字符串,这会改变您的预期结果。

但是除此之外,您还会遇到错误,因为您在 C# 中的 SQL 中有 CREATE PROC 而不是 CREATE PROCEDURE

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-29
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 2014-12-22
    • 2011-09-29
    • 1970-01-01
    相关资源
    最近更新 更多