【问题标题】:How can I pass an unkown number of arguments to C#'s "Database.Open("DatabaseName").Query()" method?如何将未知数量的参数传递给 C# 的 "Database.Open("Database Name").Query()" 方法?
【发布时间】:2013-07-11 21:39:33
【问题描述】:

我尝试用谷歌搜索找到解决方案,但我得到的只是完全不相关的结果或涉及二维数组的结果,比如这里的一个:http://social.msdn.microsoft.com/Forums/vstudio/en-US/bb4d54d3-14d7-49e9-b721-db4501db62c8/how-does-one-increment-a-value-in-a-two-dimensional-array,它不适用。

假设我已声明:

var db = Database.Open("Content");
var searchTerms = searchText.Split('"').Select((element, index) => index % 2 == 0 ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : new string[] { element }).SelectMany(element => element).ToList();
int termCount = searchTerms.Count;

(注意: 关于searchTerms,您真正需要知道的是,它包含用户在搜索栏中输入的许多搜索词。所有 LINQ 表达式都是这样做是确保包裹在 qoutes 中的文本被视为单个搜索词。为了这个问题的目的,实际上没有必要知道所有这些。)

然后我编译了(使用 for 循环循环 searchTerms 列表中的每个项目数)一个用作SELECT SQL 查询的字符串。

这是一个示例,它显示了使用 @0, @1, etc. 占位符编译此字符串的一部分,以便我的查询被参数化。

searchQueryString = "SELECT NULL AS ObjectID, page AS location, 'pageSettings' AS type, page AS value, 'pageName' AS contentType, ";

for (int i=0; i<termCount; i++)
{
    if(i != 0)
    {
        searchQueryString += "+ ";
    }

    searchQueryString += "((len(page) - len(replace(UPPER(page), UPPER(@" + i + "), ''))) / len(@" + i + ")) ";
}

searchQueryString += "AS occurences ";

(注意: 关于上述代码,你真正需要知道的只是我将 i 的递增值连接到 em> @ 动态编译占位符值的符号。)

以上所有方法都可以正常工作,但后来,我必须使用类似的东西(只是我不知道在运行之前我需要多少参数):

foreach (var row in db.Query(searchQueryString, searchTerms[0]))
{
    @row.occurences
}

澄清: 我需要一些额外的参数(即,除了 searchQueryString 参数)等于项目的数量在 searchTerms 列表AND中,它们必须引用正确的索引(有效地从最低到最高依次引用每个索引,当然,用逗号分隔。 )

此外,我当然需要使用递增值来引用列表的适当索引,如果我什至可以做到那么远,我也不知道该怎么做。我可以为此使用i++ 吗?

我知道 C# 很强大,但也许我要求的太多了?

【问题讨论】:

  • 只是一个评论,你可能已经考虑过了,哎呀,这甚至可能是最后的手段。但是……这听起来太笼统了。这是某种通用数据层吗?你确定这不会是其中之一,“希望我没有那样做”有点像在某个地方的轨道上。也许这一切都被考虑过了,我只是想提供帮助,仅此而已。有一些很棒的 ORM 可以更好地完成所有这些工作。
  • @Mark 首先,感谢您的考虑。我永远不会也永远不会(在未来的任何时候)对“陷阱”感到满意!时刻,因此非常欢迎您的建议,但是,这是一种“不得已”的事情(主要是因为 WebMatrix 附带的 SQL Server CE 不支持全文索引)。话虽如此,但我确实知道什么是 ORM。
  • 意味着您的查询可以用 LINQ 而不是 SQL 编写。如果是 EF,您将使用实现 IQueryable 的对象。您可以使用与现在几乎相同的方式向 where 子句添加位,但方式要好得多。然后,当您执行 ORM 时,会根据您设置 IQueryable 的方式为您编写实际的 SQL 查询。希望这会有所帮助。
  • @Mark 不幸的是,我对 LINQ 了解不多。我需要 SQL 的 ORDER BY 子句的强大功能(虽然,如果 LINQ 有非常相似的东西,我不会感到惊讶)。

标签: c# asp.net sql webmatrix asp.net-webpages


【解决方案1】:

对可变数量的参数使用 params 关键字。使用 params,传递给 any 函数的参数被编译器更改为临时数组中的元素。

static int AddParameters(params int[] values)
{
    int total = 0;
    foreach (int value in values)
    {
      total += value;
    }
    return total;
}

并且可以称为

int add1 = AddParameters(1);
int add2 = AddParameters(1, 2);
int add3 = AddParameters(1, 2, 3);
int add4 = AddParameters(1, 2, 3, 4);

//-----------基于以下cmets编辑的回复--- 您可以使用类似这样的东西与 SQL 一起使用

void MYSQLInteractionFunction(String myConnectionString)
 {
    String searchQueryString = "SELECT NULL AS ObjectID, page AS location, 'pageSettings' AS type, page AS value, 'pageName' AS contentType, ";
    SqlConnection myConnection = new SqlConnection(myConnectionString);
    SqlCommand myCommand = new SqlCommand(searchQueryString, myConnection);
    myConnection.Open();

    SqlDataReader queryCommandReader = myCommand.ExecuteReader();
    // Create a DataTable object to hold all the data returned by the query.
    DataTable dataTable = new DataTable();

    // Use the DataTable.Load(SqlDataReader) function to put the results of the query into a DataTable.
    dataTable.Load(queryCommandReader);

    Int32 rowID = 0; // or iterate on your Rows - depending on what you want
    foreach (DataColumn column in dataTable.Columns)
    {
       myStringList.Add(dataTable.Rows[rowID][column.ColumnName] + " | ");
       rowID++;
     }
     myConnection.Close();

     String[] myStringArray = myStringList.ToArray();
     UnlimitedParameters(myStringArray);
}

static void UnlimitedParameters(params string[] values)
{
    foreach (string strValue in values)
    {
        // Do whatever you want to do with this strValue
     }
 }

【讨论】:

  • 感谢您的帮助,但恐怕需要进一步澄清(对此我深表歉意),但正如您所写(例如):int add3 = SumParameters(1, 2, 3);我不会知道 要包含多少个参数。也许我只是还不明白你的答案?
  • @VoidKing:这就是params 所做的;在此示例中,您可以根据需要传递任意数量的 int 值。该方法用于处理传入的任何数量的参数。
  • 如果您不知道需要多少个参数,您可以创建一个列表并在运行时填充它,最后制作一个 Int 数组并传递给此函数... List myIntList = new List(); myIntList.Add(2); myIntList.Add(3); myIntList.Add(4); int[] myIntArray = myIntList.ToArray(); int mySum = AddParameters(myIntArray);
  • 我很高兴听到这是可能的,非常感谢你的帮助,我不想再麻烦你了,但这是我第一次被介绍给@987654327 @。如果您可以在答案中附加一个示例,说明在调用实际的 db.Query(searchQueryString, argument1, argument2, etc., etc.) 方法之前和期间我需要做的事情,如果它有效,我将很乐意投票并接受。我的计划是逐行检查,以便更好地理解代码的工作原理。
  • 另外请记住,我的 searchTerms 列表是一个字符串列表,而不是整数(如果这很重要),它们是我在我的数据库中测试的搜索词。
【解决方案2】:

我不确定我是否完全理解您对问题的需求,但看起来您正在用另一个值替换 SQL 中的一系列占位符。如果是这种情况,您可以使用 String.Format 替换如下值:

        object val = "a";
        object anotherVal = 2.0;
        var result = string.Format("{0} - {1}", new[] { val, anotherVal });

这样,您可以通过简单地将参数数组创建为正确的大小来替换任意数量的值。

如果您正在动态创建 SQL 查询,那么您需要警惕 SQL 注入,并且从这个角度来看,将用户提供的文本直接替换到查询中是有点禁忌的。避免这种情况的最佳方法是在查询中使用参数,然后自动对其进行清理以防止 SQL 注入。不过,您仍然可以使用“参数数组”参数来实现您所需要的,例如:

    public IDataReader ExecuteQuery(string sqlQuery, params string[] searchTerms)
    {
        var cmd = new SqlCommand { CommandText = sqlQuery };

        for (int i = 0; i < searchTerms.Length; i++)
        {
           cmd.Parameters.AddWithValue(i.ToString(), searchTerms[i]);
        }

        return cmd.ExecuteReader();
    }

显然,如果需要,您也可以根据 searchTerms 数组的长度在此方法中构建 sql 字符串

【讨论】:

  • 我已经更新了我的问题,希望能得到更多的澄清。
  • 你的答案还适用吗?你看到我的澄清说明了吗?
  • 天哪,我想我刚刚意识到你在做什么!它似乎非常适合我的场景,但是,我有一个主要问题必须在我使用它之前(并且绝对是之前)回答。以这种方式使用 String.Format 是否会像在我上面的示例中使用参数化查询一样有效地防止 SQL 注入?
  • 使用 string.Format 不能防止 SQL 注入。这是个坏主意。
  • 嗨@VoidKing,如果您使用这种方法来创建动态SQL,那么以这种方式插入值不会阻止SQL 注入。我在使用任意数量的参数创建 Sql 命令的答案中添加了一个示例。
【解决方案3】:

好吧,对于这个问题可能看起来多么复杂,答案最终变得非常简单。我想这很容易被忽视,因为我以前从未这样做过,其他人可能认为这太明显了,不是我想要的。但是,我以前从未尝试过传递可变长度的参数,甚至不知道它是否可能(好吧,我想我知道它是可能的,但可能与我的方法相距甚远。知道)。

无论如何,我都试过了:

foreach (var row in db.Query(searchQueryString, searchTerms)) //searchTerms is a list of strings.
{
    //Do something for each row returned from the sql query.
}

假设如果它可以处理可变长度数量的参数(请记住,在 Database.Query() 方法中的第一个参数之后传递的每个参数都被视为查询字符串中占位符的填充符(例如,@0, @1, @2, etc.)它可以接受如果可以从数组中,则从列表中。

这个假设我真的不能再错了,因为传递一个列表会引发错误。但是,当我最终崩溃,将列表转换为数组并尝试传递数组而不是列表时,我感到很惊讶。

确实,这是对我最初问题的简短回答,如果我只是给它一个数组,它很容易工作(有点太容易了,我想这就是为什么我如此确定它不会'不工作):

string[] searchTermsArray = searchTerms.ToArray();

foreach (var row in db.Query(searchQueryString, searchTermsArray)) //searchTermsArray is an array of strings.
{
    //Do something for each row returned from the sql query.
}

上面的sn-p代码确实是成功回答我原来的问题所需要的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-09
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    相关资源
    最近更新 更多