C#C0246使用列表框过滤DataGridView时,其项目来自SQL Server

问题描述

我与您分享了一段有效的代码,除了我试图在列表框项目中循环的那一部分。这就是为什么我在这里向您寻求帮助。 最近,我从VBA切换到了C#,因此我对此仍然很陌生,并且不了解所有内容。 因此,以下代码连接到我的SQL Server数据库,并在列表框和DataGridView中获取数据。我也可以使用两个文本框进行过滤。 所以现在我在列表框中有项目,在DataGridview中有数据库视图。我想用Listbox的项目过滤DataGridview(由datatable填充)。我只想念一个愚蠢的部分。为什么我得到此CS0246“找不到ListItem”

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Configuration;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsAppTest
{

public partial class Form1 : Form

{
   //Initialize the component and display the items within my listbox CS_Bonds_listBox
    public Form1()
    {
        InitializeComponent();
        string connetionString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
        SqlConnection conn = new SqlConnection(connetionString);
        conn.Open();
        DataSet ds = new DataSet();
        SqlDataAdapter adapter = new SqlDataAdapter(
        "SELECT DISTINCT RatingProvider FROM Bonds",conn);
        adapter.Fill(ds);
        this.CS_Bonds_listBox.DataSource = ds.Tables[0];
        this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
    }

    private void Form1_Load(object sender,EventArgs e)
    {

    }

    DataTable dtTEST = new DataTable();

// Next,when clicking on my button Connect,I retrieve my db into a Datatable that is displayed within //the Datagridview1

    private void buttonConnect_Click(object sender,EventArgs e)
    {
        string connetionString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";
        SqlConnection cnn= new SqlConnection(connetionString);
        cnn.Open();
        MessageBox.Show("Connection Open  !");
        String sql = "Select * from Bonds";
        SqlCommand command = new SqlCommand(sql,cnn);
        SqlDataAdapter sqlDA = new SqlDataAdapter();
        sqlDA.SelectCommand = command;
        sqlDA.Fill(dtTEST);
        dataGridView1.DataSource = dtTEST;

        cnn.Close();
    }

    private void ISIN_Bonds_textBox_TextChanged(object sender,EventArgs e)
    {
        DataView dv = dtTEST.DefaultView;
        dv.RowFilter = "ISIN LIKE '" + ISIN_Bonds_textBox.Text + "%'";
        dataGridView1.DataSource = dv;
    }

    private void Ticker_Bonds_textBox_TextChanged(object sender,EventArgs e)
    {
        DataView dv1 = dtTEST.DefaultView;
        dv1.RowFilter = "Ticker LIKE '" + Ticker_Bonds_textBox.Text + "%'";
        dataGridView1.DataSource = dv1;
    }


    private void CS_Bonds_listBox_SelectedIndexChanged(object sender,EventArgs e)
    {
        string conString = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";           
        string query = "SELECT ISIN,Ticker,CrediSight,FROM Bonds";
        string condition = string.Empty;

        foreach (ListItem item in CS_Bonds_listBox.Items)
        {
            condition += item.Selected ? string.Format("'{0}',",item.Value) : "";
        }

        if (!string.IsNullOrEmpty(condition))
        {
            condition = string.Format(" WHERE Country IN ({0})",condition.Substring(0,condition.Length - 1));
        }

        using (SqlConnection con = new SqlConnection(conString))
        {
            using (SqlCommand cmd = new SqlCommand(query + condition))
            {
                using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
                {
                    cmd.Connection = con;
                    using (DataTable dt = new DataTable())
                    {
                        sda.Fill(dt);
                        dataGridView1.DataSource = dt;
                        //dataGridView1.DataBind();
                    }
                }
            }
        }
    }
 
  }
}

解决方法

此行有问题:

foreach (ListItem item in CS_Bonds_listBox.Items)

ListItem是WebForms的东西,而您的应用程序是WinForms的东西;您的列表框不包含ListItem对象的列表,因此即使导入了相关的Web名称空间,此行代码也无法实现。

因为已将列表框绑定到数据表,所以显示的列表中充满了DataRowView对象,因此需要进行处理。 DataRowView具有Row属性,该属性为您提供基础行,而基础行又可以通过列名进行访问。

另外,为了使您的生活更轻松,列表框具有SelectedItems属性,因此您无需检查每个项目是否被选中:

foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
  var dr = drv.Row as DataRow;
  var rp = dr["RatingProvider"]; 
  condition += $"'{rp}'," 
}

由于这种情况,您的条件将最终以逗号结尾,因此在使用它构建IN子句之前,请先对其进行修剪:

condition = condition.TrimEnd(',');

如果用户设法更改列表项中显示的文本,则此技术可能会受到SQL Injection黑客的攻击。

处理该问题的更好方法是通过参数化。您可以这样:

var cmd  = new SqlCommand("SELECT * FROM table WHERE Country IN(",connStr);

int i = 0;
foreach (DataRowView drv in CS_Bonds_listBox.SelectedItems)
{
  var dr = drv.Row as DataRow;
  var rp = dr["RatingProvider"]; 
  cmd.CommandText += $"@p{i},";
  cmd.Parameters.Add($"@p{i}",SqlDbType.VarChar).Value = rp;
  i++;
}

cmd.CommandText = cmd.CommandText.TrimEnd(',') + ")";
using(var da = new SqlDataAdapter(cmd))
{
  var dt = new DataTable();
  da.Fill(dt);
  someGridView.DataSource = dt;
}

这将构建一个类似于SELECT * FROM table WHERE Country IN(@p0,@p1,@p2....的sql,即我们在其中串联了参数占位符,而不是在其中串联了值。与此同时,我们用参数值填充了参数集合

这还意味着我们的数据库不能为hacked via our program,并且当用户选择名称为Cote d'Ivoire

的国家/地区时,我们的应用程序不会死于堆中

需要注意一些其他事情来整理代码:

SqlDataAdapter可以采用字符串SQL和字符串connection-string。您无需为此编写SqlCommand。您无需为此打开和关闭连接;它知道如何自己做所有这一切。我仅使用SqlCommand,因为我在进行过程中正在构建参数集合。通常我会做using(var da = SqlDataAdapter("SELECT...","Server=.."),因为它会使事情变得井井有条。

例如您的构造函数可以很简单:

//put this here once
private string _connStr = @"Data Source=my_server;Initial Catalog=my_db;Integrated Security=SSPI";

public Form1()
{
    InitializeComponent();
    
    var dt = new DataTable();
    using(var da = new SqlDataAdapter("SELECT DISTINCT RatingProvider FROM Bonds",_connStr))
      adapter.Fill(dt);
    this.CS_Bonds_listBox.DataSource = dt;
    this.CS_Bonds_listBox.DisplayMember = "RatingProvider";
}

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...