2016-04-23 134 views
4

我正在使用C#編寫Windows窗體項目。我正嘗試從數組中插入多個記錄到SQL Server數據庫中。使用for循環插入多個記錄到SQL Server數據庫中

進入第一行後,我得到一個異常

@UserID已經宣佈。變量名稱在查詢批處理或存儲過程中必須是唯一的。

數據庫中的主鍵沒有問題,因爲UserID不是主鍵。

這就是我想要做的。

public static void featuresentry() 
{ 
    SqlConnection connection = new SqlConnection(HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString); 

    SqlCommand command = new SqlCommand(); 
    connection.Open(); 

    try 
    { 
     command = connection.CreateCommand(); 

     for (int i = 0; i < Details.modelKeyPoints.Size; i++) 
     { 
      command.CommandText = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)"; 

      command.Parameters.AddWithValue("@UserID", Details.ID); 
      command.Parameters.AddWithValue("@Angle", Convert.ToDouble(Details.modelKeyPoints[i].Angle)); 
      command.Parameters.AddWithValue("@ClassID", Convert.ToDouble(Details.modelKeyPoints[i].ClassId)); 
      command.Parameters.AddWithValue("@Octave", Convert.ToDouble(Details.modelKeyPoints[i].Octave)); 
      command.Parameters.AddWithValue("@PointX", Convert.ToDouble(Details.modelKeyPoints[i].Point.X)); 
      command.Parameters.AddWithValue("@PointY", Convert.ToDouble(Details.modelKeyPoints[i].Point.Y)); 
      command.Parameters.AddWithValue("@Response", Convert.ToDouble(Details.modelKeyPoints[i].Response)); 
      command.Parameters.AddWithValue("@Size", Convert.ToDouble(Details.modelKeyPoints[i].Size)); 

      command.ExecuteNonQuery(); 
     } 
    } 
    catch (Exception) 
    { 
     throw; 
    } 
    finally 
    { 
     if (connection.State == ConnectionState.Open) 
     { 
      connection.Close(); 
     } 
    } 
} 
+4

我想,如果你把'命令= connection.CreateCommand();'裏面的for循環,它會工作。問題是你只循環了命令參數,所以它試圖向現有的命令添加更多的參數,但它們已經在那裏。所以你需要在每個循環中創建一個新的命令。 –

+1

@UnicornoMarley,是的,這是問題。很好的捕獲,發佈作爲答案。 – Rahul

+0

另一種選擇是將所有的命令和參數創建移到循環之外,只更新值並在循環內執行。這樣你就不會持續創建對象的新實例。 – gmiley

回答

5

你應該這樣做正確

  • 定義參數一次外循環
  • 定義值的參數循環內每個迭代
  • 使用using(...) { ... }塊擺脫try ... catch ... finallyusing塊將確保適當和快速處理你的課程,當不再n eeded)
  • 停止使用try...catch,如果你不是真正處理例外 - 只是重新拋出他們(沒有任何意義)

試試這個代碼:

public static void featuresentry() 
{ 
    string connectionString = HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString; 
    string insertQuery = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)"; 

    using (SqlConnection connection = new SqlConnection(connectionString)) 
    using (SqlCommand command = new SqlCommand(insertQuery, connection)) 
    { 
     // define your parameters ONCE outside the loop, and use EXPLICIT typing 
     command.Parameters.Add("@UserID", SqlDbType.Int); 
     command.Parameters.Add("@Angle", SqlDbType.Double); 
     command.Parameters.Add("@ClassID", SqlDbType.Double); 
     command.Parameters.Add("@Octave", SqlDbType.Double); 
     command.Parameters.Add("@PointX", SqlDbType.Double); 
     command.Parameters.Add("@PointY", SqlDbType.Double); 
     command.Parameters.Add("@Response", SqlDbType.Double); 
     command.Parameters.Add("@Size", SqlDbType.Double); 

     connection.Open(); 

     for (int i = 0; i < Details.modelKeyPoints.Size; i++) 
     { 
      // now just SET the values 
      command.Parameters["@UserID"].Value = Details.ID; 
      command.Parameters["@Angle"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Angle); 
      command.Parameters["@ClassID"].Value = Convert.ToDouble(Details.modelKeyPoints[i].ClassId); 
      command.Parameters["@Octave"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Octave); 
      command.Parameters["@PointX"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.X); 
      command.Parameters["@PointY"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.Y); 
      command.Parameters["@Response"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Response); 
      command.Parameters["@Size"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Size); 

      command.ExecuteNonQuery(); 
     } 
    } 
} 
+2

甚至更​​好,使用一個表值參數,並執行一個單一的語句... –

0

您需要在循環外部添加Command參數或在循環內部聲明Command。

在您需要更新這樣每個參數的值,第一種情況:

oleDbCommand1.Parameters["@UserID"].Value = Details.ID; 

而一旦新的值設置執行命令。

2

如果你把command = connection.CreateCommand();放在你的for循環中,它就會工作。問題是你只循環了命令參數,所以它試圖向現有的命令添加更多的參數,但它們已經在那裏。所以你需要在每個循環中創建一個新的命令。

0

爲了獲得最高性能,你可以考慮BulkInsert。這可以確保您的插入操作儘可能快,因爲任何已發出的查詢都會有一些開銷(大型查詢通常會比許多小型查詢執行得更快)。它應該看起來像下面這樣:

1)here定義AsDataTable擴展方法:

public static DataTable AsDataTable<T>(this IEnumerable<T> data) 
    { 
     PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); 
     var table = new DataTable(); 
     foreach (PropertyDescriptor prop in properties) 
      table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); 
     foreach (T item in data) 
     { 
      DataRow row = table.NewRow(); 
      foreach (PropertyDescriptor prop in properties) 
       row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; 
      table.Rows.Add(row); 
     } 
     return table; 
    } 

2)執行這樣的實際BulkInsert(沒有測試):

using (SqlConnection connection = new SqlConnection(connectionString)) 
{ 
    connection.Open(); 
    SqlTransaction transaction = connection.BeginTransaction(); 

    using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction)) 
    { 
     bulkCopy.BatchSize = 100; 
     bulkCopy.DestinationTableName = "dbo.FEATURES"; 
     try 
     { 
      // define mappings for columns, as property names/generated data table column names 
      // is different from destination table column name 
      bulkCopy.ColumnMappings.Add("ID","UserID"); 
      bulkCopy.ColumnMappings.Add("Angle","Angle"); 
      // the other mappings come here 

      bulkCopy.WriteToServer(Details.modelKeyPoints.AsDataTable()); 
     } 
     catch (Exception) 
     { 
      transaction.Rollback(); 
      connection.Close(); 
     } 
     } 

     transaction.Commit(); 
} 

當然,如果使用convention over configuration(對象屬性名稱將完全匹配目標表列名稱),則不需要映射。

0

您可以通過將數據作爲xml字符串發送並將其轉換爲sql中的存儲過程中的表來執行此操作。例如: 假設我派多行到一個SQL表中添加/更新那麼這裏的步驟:

  1. 轉換類或類的列表到一個XML字符串中使用下面的方法:

    public static string SerializeObjectToXmlString(object value) 
    
          { 
          var emptyNamepsaces = new XmlSerializerNamespaces(new[] { 
                XmlQualifiedName.Empty }); 
    
        var serializer = new XmlSerializer(value.GetType()); 
        var settings = new XmlWriterSettings(); 
        settings.Indent = true; 
        settings.OmitXmlDeclaration = true; 
    
        using (var stream = new StringWriter()) 
        using (var writer = XmlWriter.Create(stream, settings)) 
        { 
         serializer.Serialize(writer, value, emptyNamepsaces); 
         return stream.ToString(); 
        } 
    } 
    
  2. 現在在發送數據到數據庫轉換您的類對象轉換爲XML字符串 (在這裏我使用實體框架在我的代碼,你可以做到這一點不使用它):

    bool AddUpdateData(List<MyClass> data) 
    { 
        bool returnResult = false; 
        string datatXml = Helper.SerializeObjectToXmlString(data); 
        var sqlparam = new List<SqlParameter>() 
           { 
        new SqlParameter() { ParameterName = "dataXml", Value = datatXml} 
    
           }; 
        var result = this.myEntity.Repository<SQL_StoredProc_ComplexType>().ExecuteStoredProc("SQL_StoredProc", sqlparam); 
        if (result != null && result.Count() > 0) 
        { 
         returnResult = result[0].Status == 1 ? true : false; 
        } 
        return returnResult; 
    } 
    
  3. 現在您的SQL代碼:

3.1聲明表變量:

DECLARE @tableVariableName TABLE 
(
    ID INT, Name VARCHAR(20) 
) 

3.2插入你的XML字符串轉換成表變量

INSERT INTO @tableVariableName 
SELECT 
    Finaldata.R.value ('(ID/text())[1]', 'INT') AS ID, 
    Finaldata.R.value ('(Name/text())[1]', 'VARCHAR(20)') AS Name 
FROM @MyInputXmlString.nodes ('//ArrayMyClass/MyClass') AS Finaldata (R) 

3.3最後插入此表中的值到你的sql表中

INSERT INTO MyTable (ID, Name)     
SELECT ID, Name   
FROM @tableVariableName 

這將節省您一次又一次使用for循環打擊數據庫的工作量。

希望它會幫助你

+0

這是測試,它爲我工作 –

相關問題