【问题标题】:Inserting Multiple Records into SQL Server database using for loop使用 for 循环将多条记录插入 SQL Server 数据库
【发布时间】:2016-08-17 09:20:36
【问题描述】:

我正在使用 C# 编写一个 Windows 窗体项目。我正在尝试将多个记录从数组中插入 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();
        }
    }
}

【问题讨论】:

  • 我认为如果你将command = connection.CreateCommand(); 放在你的 for 循环中,它会起作用。问题是你只循环命令参数,所以它试图向你现有的命令添加更多参数,但它们已经在那里了。所以你需要在每个循环中创建一个新命令。
  • @UnicornoMarley,是的,这就是问题所在。不错的收获,将其发布为答案。
  • 另一种选择是将所有命令和参数创建移到循环之外,只更新值并在循环内执行。这样您就不会不断地创建新的对象实例。
  • @Unicorno 。谢谢您的帮助。它奏效了。

标签: c# sql-server database winforms


【解决方案1】:

您需要在循环外添加 Command 参数或在循环内声明 Command。

在第一种情况下,您需要像这样更新每个参数的值:

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

并在设置新值后执行命令。

【讨论】:

    【解决方案2】:

    如果你将command = connection.CreateCommand(); 放在你的 for 循环中,它将起作用。问题是你只循环命令参数,所以它试图向你现有的命令添加更多参数,但它们已经在那里了。所以你需要在每个循环中创建一个新命令。

    【讨论】:

      【解决方案3】:

      您应该正确这样做:

      • 在循环外定义参数一次
      • 在每次迭代的循环内定义参数的
      • 使用using(...) { ... } 块删除try ... catch ... finallyusing 块将确保在不再需要时正确快速地处理您的课程)
      • 如果您实际上没有处理异常,请停止使用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();
              }
          }
      }
      

      【讨论】:

      • 或者更好的是,使用表值参数并只执行一条语句...
      • @ZoharPeled 很遗憾你没有按照这些思路提供答案!
      【解决方案4】:

      为了获得最大的性能,您可以考虑使用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(对象属性名称将与目标表列名称完全匹配),则不需要映射。

      【讨论】:

        【解决方案5】:

        您可以通过将数据作为 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 循环一次又一次地访问数据库的工作。

        希望对你有帮助

        【讨论】:

          猜你喜欢
          • 2023-04-03
          • 1970-01-01
          • 1970-01-01
          • 2022-01-05
          • 1970-01-01
          • 1970-01-01
          • 2019-02-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多