使用for循环将多个记录插入SQL Server数据库

时间:2016-04-23 20:17:52

标签: c# sql-server database winforms

我正在使用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();
        }
    }
}

5 个答案:

答案 0 :(得分:6)

您应该正确

  • 在循环外
  • 定义一次参数
  • 为每次迭代定义循环内参数的
  • 使用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();
        }
    }
}

答案 1 :(得分:2)

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

答案 2 :(得分:0)

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

在第一种情况下,您需要更新每个参数的值,如下所示:

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

设置新值后执行命令。

答案 3 :(得分: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(对象属性名称将与目标表列名称完全匹配),则不需要映射。

答案 4 :(得分: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 string(这里我在我的代码中使用实体框架,你可以在不使用它的情况下执行此操作):

    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代码:

  4. 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循环一次又一次地重击数据库的努力。

    希望它能帮到你