从 Windows 窗体上的单个按钮将数据多次插入 SQL 表

Posted

技术标签:

【中文标题】从 Windows 窗体上的单个按钮将数据多次插入 SQL 表【英文标题】:Inserting data multiple times into an SQL table, from a single button on Windows Forms 【发布时间】:2011-07-26 01:52:15 【问题描述】:

我正在将Windows Forms 表单中的数据插入到 SQL 数据库中,如下所示:

public partial class Form1 : Form

    SqlConnection c = new SqlConnection();
    string q = "Trusted_Connection = true; ";

    public Form1()
    
        InitializeComponent(); c.ConnectionString = q;
        MessageBox.Show("Connecting Database");
    

    private void button1_Click(object sender, EventArgs e)
    
        string w = "insert into checkmultiuser(username) values (@username)";

        SqlCommand cmd = new SqlCommand(w, c);
        cmd.Parameters.Add("@username", SqlDbType.VarChar);
        cmd.Parameters["@username"].Value = textBox1.Text;
        cmd.ExecuteReader();
    

但是,当我再次单击该按钮时,它会显示“已经有一个打开的 DataReader 与此命令关联,必须先关闭。”。

我该如何处理?

*更新:* 我已将按钮单击事件代码更改为如下:

private void button1_Click(object sender, EventArgs e)

    **c.Open();**

    string w = "insert into checkmultiuser(username) values (@username)";

    SqlCommand cmd = new SqlCommand(w, c);

    cmd.Parameters.Add("@username", SqlDbType.VarChar);
    cmd.Parameters["@username"].Value = textBox1.Text;
    //cmd.ExecuteNonQuery();
    cmd.ExecuteReader();
    **c.Close();**

它的缺点是什么?一种是在单击按钮时一次又一次地打开和关闭连接。

【问题讨论】:

在更新的代码中,请完全删除 cmd.ExecuteReader() 行并取消注释 cmd.ExecuteNonQuery() 行。 【参考方案1】:

阅读器需要一个开放的连接,因为您一次只检索一个结果。你会想要为插入、删除和更新做一个ExecuteNonQuery()。之后您还需要关闭连接。如果您打算进行多次插入,另一种方法是将插入包装到一个循环中。

有几种方法可以做到这一点。如果您打算一遍又一遍地重用相同的连接对象和命令对象,这种方式很有用:

public partial class Form1 : Form

    SqlConnection _cn;
    SqlCommand _cmd;

    const string ConnString = "Enter your connection string here";
    readonly string _insertQuery;
    const string UsernameParm = "@username";

    public Form1()
    
        InitializeComponent(); 
        _cn = new SqlConnection(ConnString);

        _cmd = new SqlCommand(InsertQuery, _cn);        
        _cmd.Parameters.Add(UsernameParm, SqlDbType.VarChar);

        _insertQuery = String.Format("INSERT INTO checkmultiuser(username) VALUES (0)",
                                     UsernameParm);
    

    private void button1_Click(object sender, EventArgs e)
            
        _cmd.Parameters[UsernameParm].Value = textBox1.Text;

        try
        
            _cn.Open();
            _cmd.ExecuteNonQuery();
        
        catch (Exception ex) // probably best to catch specific exceptions
        
            // handle it
        
        finally
        
            _cn.Close();
        
    

请务必处理掉连接和命令对象(当表单关闭或对您的应用程序最有意义的时候)。

使用块是一种更安全的选择,但它们每次都会释放对象(尽管连接默认使用连接池):

public partial class Form1 : Form

    const string ConnString = "Enter your connection string here";
    readonly string _insertQuery;
    const string UsernameParm = "@username";

    public Form1()
    
        InitializeComponent();             

        _insertQuery = String.Format("INSERT INTO checkmultiuser(username) VALUES (0)",
                                     UsernameParm);
    

    private void button1_Click(object sender, EventArgs e)
      
        using (var cn = new SqlConnection(ConnString))
        
            using (var cmd = new SqlCommand(InsertQuery, cn))
            
                cmd.Parameters.Add(UsernameParm, SqlDbType.VarChar);
                cmd.Parameters[UsernameParm].Value = textBox1.Text;

                cn.Open();
                cmd.ExecuteNonQuery();
            
        
    

任何组合都可以。您可以设置一次连接,然后将命令对象包装在 using 块中。我知道有些人不喜欢嵌套 using 块(因为在幕后是 try(try finally) finally))。

【讨论】:

另外,他应该关闭SqlCommand,将其放入using 更新了几个代码示例,其中一个显示了 using 语句。 @Jason Down:非常感谢您的出色回答,此外,读者是否在每次需要从表中检索一行时都尝试打开连接? @Jason Down:我只是在使用 cmd.ExecuteReader() ,但没有创建任何阅读器,这种方法会自动创建阅读器吗? @sqlchild - 对于您的第一个问题:ExecuteReader 调用打开连接并保持打开状态。之后的每次读取都不需要重新打开连接。对于您的第二个问题:一旦您调用 ExecuteReader,它就会创建一个读取器对象,即使您不通过将其分配给变量来使用它。我不确定这个对象什么时候会被标记为垃圾回收。如果你不使用它,最好不要创建它。您最好将 ExecuteReader 更改为 ExecuteNonQuery 以获取 Insert 命令。我想不出一个好的论据来反对它。【参考方案2】:

首先,您应该使用ExecuteNonQuery 来处理INSERTS、UPDATES 和DELETE。当您不期望从数据库返回值时使用 ExecuteNonQuery。

但如果你真的想在另一种情况下使用 DataReader,你应该像下面这样使用它。

using (SqlDataReader reader= cmd.ExecuteReader())

        while (reader.Read())
        
            //Do the reading
        
 

这可确保在完成预期工作后关闭SqlDataReader

另一个可能将其修复为连接字符串中的set MARS (Multiple Active Result Sets) to true。

【讨论】:

【参考方案3】:

你可以试试:

cmd.ExecuteReader(CommandBehavior.CloseConnection);

【讨论】:

【参考方案4】:

杰森说了什么。

虽然您在那里,但也许值得将您的代码稍微更改为:

private void button1_Click(object sender, EventArgs e)


  string w = "insert into checkmultiuser(username) values (@username)";
  c.Open();
  using (SqlCommand cmd = new SqlCommand(w, c))
  
     cmd.Parameters.Add("@username", SqlDbType.VarChar);
     cmd.Parameters["@username"].Value = textBox1.Text;
     cmd.ExecuteNonQuery();
  


确保物品得到正确处理。

也许也可以将它放在try 块中,然后关闭finally c.Close(); 中的连接

【讨论】:

@sqlchild 在这种情况下,我会修改我的代码,使其与 Jason 的代码非常相似,Jason 现在被标记为答案——我对他的支持。很高兴你把它整理好了:) 先生,如果我在这里使用 SELECT 命令,那么我应该使用数据集,因为它是断开连接的环境,还是在用户直接从数据库中单击按钮时向用户显示数据?哪个更快更好?

以上是关于从 Windows 窗体上的单个按钮将数据多次插入 SQL 表的主要内容,如果未能解决你的问题,请参考以下文章

使用窗体上的按钮将值插入Microsoft Access数据库

无法从 Windows 窗体将数据插入 SQL Server

在 C# windows 窗体中的树视图中面临问题,详细信息如下:

C# Windows 窗体使用 OracleDataAdapter 从系统数据表向 Oracle 表插入行

将字符串从控制台应用程序发送到 Windows 窗体应用程序

公用文件夹上的 Windows 窗体 app.config