以逗号分隔的列表作为数字字符串传递,以使用Npgsql在C#中作为PostgreSQL数据库的整数列表进行查询

问题描述 投票:1回答:1

在用C#编写的WinForms项目中,我正在使用Npgsql框架连接到PostgreSQL数据库。

我正在用PG数据库中的DataTable填充几个DGV。我还尝试实现一种搜索功能,当用户启动该搜索功能时,将在DGV的DataTable中搜索搜索字符串,然后根据这些结果重建DGV。在该过程搜索DataTable时,它正在构建记录ID的列表(例如,“ 1234,2345,3456”),该列表将传递给db查询,以便仅使用与用户搜索值匹配的记录来重新填充DGV。

这是我的搜索功能,用于构建ID和数据库查询的列表:

private void btnSearch_Click(object sender, EventArgs e)
{
    string searchText = txtSearchCatsGrid.Text;
    string idToAddToList = "";
    string tempIdList = "";
    string idListForQuery = "";

    DataTable dt = ((DataTable)dgvCategories.DataSource);
    string cellValue;
    int rowIndex;
    int columnIndex;

    for (rowIndex = 0; rowIndex <= dt.Rows.Count - 1; rowIndex++)
    {
        for (columnIndex = 0; columnIndex <= dt.Columns.Count - 1; columnIndex++)
        {
            cellValue = dt.Rows[rowIndex][columnIndex].ToString();

            if (searchText == cellValue)
            {
                // Set corresponding dgvCategories cell's background color to yellow
                dgvCategories[columnIndex, rowIndex].Style.BackColor = Color.Yellow;

                idToAddToList = dt.Rows[rowIndex][0].ToString();
            }
        }

        if (!string.IsNullOrEmpty(idToAddToList) && !tempIdList.Contains(idToAddToList)) tempIdList += idToAddToList + ",";

    }

    if (!string.IsNullOrEmpty(tempIdList))
    {
        // Remove the trailing comma that was added the last time an id was added to the list
        idListForQuery = tempIdList.Remove(tempIdList.Length - 1, 1);
    }

    Console.WriteLine("idListForQuery: " + idListForQuery);

    // Refresh dgvCategories with just rows from search, using idListForQuery

    string companyFilter = cboSelectCompany.Text;
    string categoryFilter = cboSelectCategory.Text;

    db categoriesData = new db();

    if (categoryFilter == "All Categories")
    {
        string catsQuery = "SELECT id, category, source_company, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
                            "FROM masterfiles.xref"+
                            " WHERE company_name = @company" +
                                " AND id IN (@idList)" +
                                //" AND id IN (SELECT UNNEST(@idList))" +
                            " ORDER BY category, old_value, source_company";
        this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter, idListForQuery);
    }
    else
    {
        string catsQuery = "SELECT id, category, source_company, old_value, old_desc, new_value, new_desc, reference1, reference2 " +
                            "FROM masterfiles.xref" +
                            " WHERE company_name = @company" +
                                " AND category = @category" +
                                " AND id IN (@idList)" +
                                //" AND id IN (SELECT UNNEST(@idList))" +
                            " ORDER BY old_value, source_company";
        this.dtCategories = categoriesData.GetDgvData(catsQuery, companyFilter, categoryFilter, idListForQuery);
    }

    dgvCategories.DataSource = this.dtCategories;

    if (dtCategories.Rows.Count == 0)
    {
        return;
    }
    else
    {
        dgvCategories.Columns[0].Visible = false;
        dgvCategories.Rows[0].Cells[0].Selected = false;
    }
}

然后在我的数据库类中,GetDgvData()函数如下所示:

public DataTable GetDgvData(string selectQuery, string companyFilter, string categoryFilter, string idFilter)
{
    using (NpgsqlConnection conn = new NpgsqlConnection(connString))
    using (NpgsqlCommand cmd = new NpgsqlCommand(selectQuery, conn))
    {
        cmd.Parameters.Add(new NpgsqlParameter("company", companyFilter));

        if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", idFilter);
        //if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add("idList", NpgsqlDbType.Array | NpgsqlDbType.Integer).Value = idFilter;
        //if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", "ARRAY[" + idFilter + "]"));

        if (categoryFilter != "All Categories") cmd.Parameters.Add(new NpgsqlParameter("category", categoryFilter));

        conn.Open();

        DataSet ds = new DataSet();

        using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd))
        {
            da.Fill(ds);
        }

        return ds.Tables[0];
    }
}

我在使用该ID列表作为查询中的参数时遇到麻烦。

您会注意到,在两个代码块中我都注释了几行。这些都是传递列表的各种尝试,所有这些尝试都会在行da.Fill(ds);

上产生错误。

[当我只在查询构建代码中使用未注释的行AND id IN (@idList)和添加参数if (!string.IsNullOrEmpty(idFilter)) cmd.Parameters.Add(new NpgsqlParameter("idList", idFilter);的“常规”方法时,也在GetDgvData()函数中进行了注释,da.Fill(ds);处的错误是NpgsqlPostgresException,“运算符不存在:integer = text'”。

经过一些研究,我尝试在查询代码中使用UNNEST()函数,并保留参数代码。当我尝试该操作时,出现异常提示该函数不存在。

[经过更多研究之后,我发现了一个将Array db类型转换为Integer db Type的代码示例,这是GetDgvData()函数的parameter部分中的注释代码的第一行。当我尝试这样做时,抛出的异常是“ System.InvalidCastException:'无法将System.String类型写为System.Int32的数组'”]

[经过更多研究之后,我尝试了GetDgvData()函数中的第三条注释行,该行用"Array[]"包围了该参数。我得到的异常是,“运算符不存在:integer = text'”。我尝试了在查询中是否包含UNNEST()函数。没有UNNEST(),我得到一个Npgsql.PostgresException,“运算符不存在:integer = text'”。使用UNNEST()我得到了PostgresException,“函数unnest(text)不存在'”。

我也将ids构造为List<>,并尝试将其传递给GetDgvData(),但是我没有正确地传递它或其他东西,因为当GetDgvData()到达行我尝试使用List<>进行操作-包括仅尝试查看控制台中的内容。

所以我真的不知道从这里还能尝试什么。

其他人已经在各自的解决方案中成功使用了上面的代码,所以我只能假设我缺少什么,但是可能是什么,我不知道。

c# arrays postgresql parameter-passing npgsql
1个回答
0
投票

有很多需要解构的东西,所以我将尝试将其归结为我认为是错误的。我希望这足以使您步入正轨。

似乎您试图使用数组,这绝对是正确的方法。问题是您使用了IN,它用于离散列表。对于阵列,您需要使用ANY

select *
from foo
where bar in (1, 2, 3)

翻译为

select *
from foo
where bar = any (array[1, 2, 3]) -- or shorthand any ('{1,2,3}')

第二,除非使用动态SQL,否则如果您调用绑定变量,则需要为其分配一个值。反之则不正确。因此,一旦在SQL中声明@category,则必须在C#中为其分配一个值。解决此问题的一种可能方法是将您的C#逻辑包装在SQL中-我不认为您会因此受到性能的影响。

您修改后的查询将如下所示:

SELECT
  id, category, source_company, old_value, old_desc, new_value, new_desc, 
  reference1, reference2 
FROM masterfiles.xref
WHERE
  company_name = @company
  AND (category = @category or @category = 'All Categories')
  AND id = any (@idList)
ORDER BY old_value, source_company

然后,我将进行的关键更改是将您的ID列表传递为整数数组或可以通过.ToArray()转换为整数数组的东西。

public DataTable GetDgvData(string selectQuery, string companyFilter, 
    string categoryFilter, int[] idFilter)
{
    using (NpgsqlConnection conn = new NpgsqlConnection(connString))
    {
        using (NpgsqlCommand cmd = new NpgsqlCommand(selectQuery, conn))
        {
            cmd.Parameters.AddWithValue("company", NpgsqlTypes.NpgsqlDbType.Varchar,
                companyFilter);
            cmd.Parameters.AddWithValue("category", NpgsqlTypes.NpgsqlDbType.Varchar,
                categoryFilter);
            cmd.Parameters.AddWithValue("idList", NpgsqlTypes.NpgsqlDbType.Array |
                NpgsqlTypes.NpgsqlDbType.Integer, idFilter);

其余代码相同。

原谅我对AddWithValue的使用在很多圈子里都皱着眉头。坦率地说,我喜欢它,并且感到反对它的论点并没有超过它的用处,特别是在PostgreSQL中。

© www.soinside.com 2019 - 2024. All rights reserved.