2016-04-12 106 views
3

我有一個SQL語句,我需要在C#中運行,並需要從C#代碼中獲取參數。我知道存儲過程是避免SQL注入的首選,但我只是想在C#中做到這一點。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 

Result is:

現在試圖在C#我遇到一個錯誤,當我嘗試運行#AddCriteriaTable程序。當我嘗試在第二的ExecuteQuery運行它拋出的最後一行:

異常信息:System.Data.SqlClient.SqlException,關鍵字「PROC」附近有語法錯誤。

爲什麼它在SQL Server中工作,但不在C#代碼中?有沒有另一種方式在C#中做到這一點?讓我知道是否有C#指導我應該遵循,因爲我仍然在學習這個C# - 數據庫工作。

enter image description here

編輯: 我知道我可以做到這一點作爲一個正常的存儲過程,並通過在一個DataTable但有團隊的問題,我不能說,它迫使我使用SP爲文本。

+0

即時猜測有CREATE PROC你得到一個錯誤的越多,你怎麼做C#調用這就是問題。在最後一個執行#computebasedoncritieria之前的所有行將執行查詢,行,最後一行將需要一個普通的查詢來獲取數據 - 你可以顯示你的C#代碼 – BugFinder

+0

我粘貼包含我的C#代碼的圖像。我還沒有編寫#computebasedoncritieria;執行#AddCriteriaTable會引發錯誤。 –

+1

鄙視你的代碼Id沒有期望像你這樣做你的程序,因爲你似乎稱它爲你,你希望你用另一個execnoquery做它,然後運行它..它似乎有一個混合在那最後一組命令 – BugFinder

回答

3

的原因,它是失敗的是要傳遞參數給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)); 

然後創建procdure:

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 
      } 
     } 
    } 
+0

其實這個詞是'PROC'。 SO中有人將其編輯爲「程序」。我現在還回來了。截圖中的錯誤來自ExecuteReader所做的第三個命令文本。我仍然不清楚爲什麼這是失敗的。我會檢查你的代碼,謝謝! –

+0

我真的很感謝您所做的顯示代碼的辛勤工作。這只是我目前無法使用SP方法(請參閱編輯)。我最初的設計與你所建議的類似,但最終不得不成爲文本。該臨時表是爲了讓兩個SP可以使用數據.. –

+0

我已經想通了,我已經在我的編輯解釋的更好,但它的要點是要傳遞的參數值是'CREATE PROC'聲明,沒有必要。 – GarethD

1

如果您在執行SQL創建通過C#的存儲過程,那麼你可能也只是通過C#執行SQL而忽略了過程。

使用存儲過程避免SQL注入的要點僅適用於存儲過程已存在於服務器上並且不是通過代碼創建它的情況。

您可以通過使用參數化查詢來避免SQL注入。 參數通過驗證數據類型來防止sql注入。所以如果你在你的代碼中插入一個整數,那麼有人試圖注入不能提供一個特殊字符的字符串,這會改變你的預期結果。

但所有分開,因爲你在你的SQL在C#中,而不是CREATE PROCEDURE