2014-03-03 43 views
4

我一直在網上搜索,但我似乎無法找到任何解釋我的問題(可能是因爲我沒有使用正確的搜索字符串),所以我在這裏張貼希望有人能幫助我。 (我的程序編寫的是使用Visual Studio 2010的C#)SqlCommand參數與String.Format

我注意到在C#中,有多種構建SQL命令的方法。

SqlConnection connection = GetAndOpenConnection(); //function containing connection string and open connection 
SqlCommand command = connection.CreateCommand(); 

到目前爲止,我沒有任何問題。我遇到的問題是CommandText。我在我的代碼中使用了幾個不同的命令(SELECT/INSERT/UPDATE/DELETE),但以SELECT爲例。

//Example 1: 
command.CommandText = String.Format("SELECT * FROM myTable WHERE name = '{0}'", "bob"); 

//Example 2: 
command.CommandText = "SELECT * FROM myTable WHERE name = @myName"; 
command.Parameters.Add(new SqlParameter("myName", "bob")); 

以上兩個例子有什麼區別? (性能明智/結構明智/等)

我問的原因是因爲在同一個.cs文件中,當我使用示例2中的方法,有時代碼正常工作,有時它不,那麼我最終會像例1中的每一個例子一樣工作。

使用這兩種方法是否有重大收益/損失?哪種方法可以完成這樣的任務?

另一個問題

好了,所以我看到方法2是更合適的方式來做到這一點。

不過,如果我使用方法2

有一個問題,我有一個循環通過List<string> names這個循環。在循環內部,當我使用方法2並添加名稱作爲參數時,出現錯誤,說明參數已經存在,無法添加。

我該如何解決這個問題?

List<string> names = new List<string> {"adam", "bob", "john"}; 
foreach(string name in names) 
{ 
    command.CommandText = "SELECT * FROM myTable WHERE name = @myName"; 
    command.Parameters.Add(new SqlParameter("myName", name)); 
    reader = command.ExecuteReader(); 

    while(reader.Read()) 
    { 
     //loop through each cell and print on the Console 
    } 
} 

而且,我知道人們提到,在參數應該是"@myName"而不是"myName"。我記得有這個問題,因爲我很困惑使用哪種方式,只能測試它。 "@myName"對我來說不起作用,但是"myName"就是這樣,這就是它現在在我仍然使用方法2的部分的代碼。我使用的是.Net 4.0,不知道這是否會有所作爲。

+0

另外你沒有正確添加參數:在每次迭代中使用後

您需要處置命令,在添加參數時它應該以'@'開頭。 – Habib

+0

http://bobby-tables.com/ – Habib

+0

你可以顯示其他問題的代碼嗎? – Andrei

回答

8

參數存在,以防止SQL注入。例如,考慮在string.Format的情況下會發生什麼情況,而不是bobTextBox1.Text,其中包含1';DROP TABLE myTable;'

如果您完全控制參數,SQL注入是不可能的,就像在您的情況下使用參數的字符串文字一樣。然而,你永遠不知道你的代碼將來如何改變,所以按照經驗法則,你應該始終堅持使用參數的更安全的方法。

如果您正面臨第二種方法的某些特定問題 - 搜索並可能在此處發佈,則很可能已有解決方案。例如,在您的代碼片段中,實際參數名稱是帶有@符號的@myName,這應該提供給SqlParameter構造函數。

更新。在您的其他問題的問題恰恰是在參數命名 - 它應該是@myName

command.Parameters.Add(new SqlParameter("@myName", name)); 

你也應該在每次迭代明確的參數集合:

command.Parameters.Clear(); 

雖然這將是更好地創建新的命令在每次迭代中避免混亂 - 請查看this thread瞭解詳情。

+0

我可能是錯的,但我認爲在每次迭代中創建命令會佔用太多空間(列表名稱相當大,我只在示例中放置小樣本大小),這就是爲什麼我在循環外部聲明SqlCommand,並在循環內部更改CommandText。我會嘗試你提供的方法。 – sora0419

+0

@ sora0419,未使用的對象將被垃圾收集,並且不會導致任何內存問題,特別是如果您將它們包裝在「使用」中進行正確處置。 – Andrei

3

方法1:容易受到SQL注入

方法2:安全的方式做SQL與更多一點的工作

BTW應該command.Parameters.Add(new SqlParameter("@myName", "bob"));

+0

我記得對此感到困惑。我不確定它的「@myName」或「myName」是否需要測試。 「我的名字」是實際爲我工作的一個,它仍然是這樣,在我的代碼中,我仍然使用方法2.(我有.Net 4.0,不知道這是否有所作爲。) – sora0419

4

如前所述,第一個(動態)查詢容易受到SQL注入攻擊。此外,每次執行時都必須重新編譯,從而提高了執行成本(並且在編譯時可能會阻塞)。

第二個(參數化的)查詢不容易受到SQL注入攻擊。此外,它的執行計劃可以是緩存,這樣它只在第一次執行時才被編譯一次。至少在緩存過期或由於某種原因而被刷新之前。

3

以上所有的答案都正確,但回答您的編輯:

using (var conn = new SqlConnection(connectionString) 
{ 
    conn.open(); //You only need to open the connection once, so we do it outside the loop 
    foreach (var name in names) 
    { 
     using (var cmd = new SqlCommand("SELECT * FROM myTable where name = @MyName", conn) 
     { 
      cmd.Parameters.AddWithValue("@MyName", name); 
      //Do something with the command 
     } 
     //The command is disposed of here 
    } 
} 
//The connection is disposed of here.