2016-04-23 128 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

这是测试,它为我工作 –

相关问题