简体   繁体   English

C#vs2012中的foreach(checklistbox.checkedItems中的变量)

[英]foreach (variable in checklistbox.checkedItems) in c# vs2012

I'm trying to make a program where the user can select several items in a combo listbox, retrieves its id from my SQL database, returns the id, and inserts the id in another table. 我正在尝试制作一个程序,用户可以在其中选择组合列表框中的多个项,从我的SQL数据库中检索其ID,返回该ID,然后将该ID插入另一个表中。 I'm not sure if this is the most effective way, and suggestions are welcome. 我不确定这是否是最有效的方法,欢迎提出建议。 However, I ran into the problem in how to complete my foreach statement. 但是,我遇到了如何完成我的foreach语句的问题。 What should I place in the position marked by X below? 我应该在下面的X标记的位置放置什么? (in the foreach statement) (在foreach语句中)

Here's my code: ( clbGeneral is the name of the checklistbox) 这是我的代码:( clbGeneral是清单clbGeneral的名称)

if (clbGeneral.CheckedIndices.Count != 0)
{
    foreach ( X in clbGeneral.CheckedItems)
    {
        con.Open();
        cmd = new SqlCommand("SELECT serv_id from services where serv_name='" + clbGeneral.SelectedItem.ToString() + "';", con);
        serv_id = (int)cmd.ExecuteScalar();

        con.Close();
        transactSQL();
        MessageBox.Show(clbGeneral.SelectedItem.ToString());
    }
}

the transactSQL function: transactSQL函数:

public void transactSQL() {
    con.Open();
    cmd = new SqlCommand("INSERT INTO transactions VALUES ('" + txtDate.Text + "','" + dataPnts.SelectedRows[0].Cells[3].Value.ToString() + "','" + dataProvs.SelectedRows[0].Cells[4].Value.ToString() + "','" +serv_id.ToString() + "');", con);
    cmd.ExecuteNonQuery();
    con.Close();
}

CheckedListBox.Items is a collection of Objects and CheckedListBox.CheckedItems returns the subset of objects in CheckedListBox Items only those items whose System.Windows.Forms.CheckState is Checked or Indeterminate . CheckedListBox.ItemsObjects的集合, CheckedListBox.CheckedItems返回System.Windows.Forms.CheckStateCheckedIndeterminate那些项目,才返回CheckedListBox Itemsobjects子集。

You can use Type var or object to represent each item in the CheckedItems and call ToString() to get the each checked Item in the foreach loop. 您可以使用Type varobject表示CheckedItems中的每个项目,并调用ToString()以获取foreach循环中的每个选中的项目。

Try This: 尝试这个:

foreach (var item in clbGeneral.CheckedItems)
{
    con.Open();
    cmd = new SqlCommand("SELECT serv_id from services where serv_name='" + item 
          .ToString() + "';", con);
    serv_id = (int)cmd.ExecuteScalar();
    con.Close();
    transactSQL();
    MessageBox.Show(item.ToString());
}
foreach (var checkedItem in clbGeneral.CheckedItems)
{

}

You don't actually have that much choice there. 您实际上在那里没有太多选择。 CheckedListBox.CheckedItems is a CheckedItemCollection which happens to store its elements as plain object s. CheckedListBox.CheckedItemsCheckedItemCollection这恰好存储其元素为纯object秒。 So the foreach will only be able to get individual items of type object from that: 因此,foreach只能从中获取object类型的单个项:

foreach (object item in clbGeneral.CheckedItems)
{ … }

Of course, you could also use var item , letting the compiler infer the type automatically. 当然,您也可以使用var item ,让编译器自动推断类型。 That won't change the variable type though, as the compiler would perform the same thinking process as I did above. 不过,这不会改变变量类型,因为编译器将执行与我上面相同的思维过程。 So item would still be an object . 因此item仍然是一个object

Of what type those objects actually are, depends on what items you put into it in the first place. 什么类型的这些对象实际上是取决于你是什么项目投入它摆在首位。 If all those items are of a certain type, you can type-cast them inside the loop. 如果所有这些项目都属于某种类型,则可以在循环内进行类型转换。

To continue further though, your SQL statement within the loop should reflect the item of that loop iteration, and not just the selected item. 但是,要继续进行下去,循环中的SQL语句应反映该循环迭代的项目,而不仅是所选的项目。 So your command could look like this—but that again depends a bit on what objects you added to the list, and if their ToString value is good enough for the query: 因此,您的命令可能看起来像这样,但这又取决于您向列表中添加了哪些对象,以及它们的ToString值对于查询是否足够好:

cmd = new SqlCommand("SELECT serv_id from services where serv_name='" + item.ToString() + "';", con);

So that was the first part of my answer. 这就是我回答的第一部分。 The next part is more of a code review, to make sure you don't do bad stuff. 下一部分是更多的代码审查,以确保您没有做不好的事情。 First of all, for SqlConnections , the recommended way to close it is by not doing it yourself, but using the connection within a using statement. 首先,对于SqlConnections ,关闭它的推荐方法是不自己做,而是在using语句中使用连接。 That way you ensure that the connection is closed even if errors might terminate the current code: 这样,即使错误可能终止当前代码,您也可以确保关闭连接:

using (var con = OpenConnection())
{
    con.Open();
    var cmd = new SqlCommand(…, con);
    cmd.ExecuteScalar();
}

Next, you really shouldn't just append some text into a SQL query. 接下来,您实际上不应该只是将一些文本附加到SQL查询中。 The chance for SQL injections is just to high. SQL注入的机会很高。 Instead, you should just use parameterized queries, which automatically take care of passing the values properly, escaping stuff as needed etc.: 相反,您应该只使用参数化查询,该查询将自动处理正确传递值,根据需要转义内容的工作,等等:

var cmd = new SqlCommand("SELECT serv_id from services where serv_name=@Name", con);
cmd.Parameters.Add("@Name", SqlDbType.VarChar).Value = item.ToString();
cmd.ExecuteScalar();

What should I place in the position marked by X below? 我应该在下面的X标记的位置放置什么?

As noted in other answers, you can just use "var" and let the compiler worry about it. 如其他答案中所述,您可以只使用“ var”并让编译器担心它。

I'm not sure if this is the most effective way, and suggestions are welcome. 我不确定这是否是最有效的方法,欢迎提出建议。

1) If I were you, I wouldn't open and close the connection inside the foreach--I'd open it just before the loop and close it just after. 1)如果我是你,则不会在foreach内部打开和关闭连接-我会在循环之前将其打开,而在循环之后将其关闭。 And, actually, I'd wrap it in a try-finally with the close inside the finally. 而且,实际上,我将其包装在try-finally中,并在其内部将其收尾。 In order for this to work, you'll need to not open and close inside of transactSQL , either. 为了使其正常工作,您也无需打开和关闭transactSQL内部。 If that breaks other parts of your code, you could introduce a new variable that tracks whether con was open or closed when you entered the method and then refers to it to decide whether or not to call open and/or close. 如果这破坏了代码的其他部分,则可以引入一个新变量,该变量在输入方法时跟踪con是打开还是关闭,然后引用该变量来决定是否调用open和/或close。

2) Instead of making 2 round trips to the database, you can do it all in 1 call. 2)您无需在数据库中进行2次往返,而是可以在一次调用中完成所有操作。 This would actually eliminate the need to call transactSQL at all. 这实际上将根本不需要调用transactSQL。 I believe the following would work for you (or at least something very close to it): 我相信以下内容对您有用(或至少很接近):

if (clbGeneral.CheckedIndices.Count != 0)
{
    con.Open();
    try
    {
        foreach (var item in clbGeneral.CheckedItems)
        {
            string sql=string.Format("INSERT INTO transactions (<column names here>) " +
            "SELECT '{0}', '{1}', '{2}', serv_id "+ // add "TOP 1" if you might get more than 1 result and only want to do 1 insert
            "FROM services " +
            "WHERE serv_name=@Name); ",
            txtDate.Text, dataPnts.SelectedRows[0].Cells[3].Value.ToString(),
            dataProvs.SelectedRows[0].Cells[4].Value.ToString(), item.ToString());

            cmd = new SqlCommand(sql, con);
            cmd.Parameters.Add("@Name", SqlDbType.VarChar).Value = item.ToString();
            cmd.ExecuteNonQuery();

            MessageBox.Show(clbGeneral.SelectedItem.ToString());
        }
    }
    finally
    {
        con.Close();
    }
}

The first time I run an ad hoc query, I like to put a debugging break point just after I've set sql and before I actually use it. 第一次运行临时查询时,我想在设置sql和实际使用它之前放置一个调试断点。 That way I can grab the value of the SQL that was generated and then copy-paste it into Enterprise Manager and make sure it looks correct and doesn't generate and compile time errors before executing. 这样,我可以获取所生成的SQL的值,然后将其复制粘贴到企业管理器中,并确保它看起来正确并且在执行之前不会生成和编译时间错误。 I'd recommend you do that here since I can't check against your DB to make sure my syntax is 100% correct :) 我建议您在这里这样做,因为我无法检查您的数据库以确保我的语法是100%正确的:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM