未同步的代码抛出索引超出数组的边界

本文关键字:边界 数组 索引 同步 代码 | 更新日期: 2023-09-27 18:16:55

在你把这个问题标记为重复之前,这里有一个棘手的部分我不明白。这个错误是零星的,我相信代码是正确的,它总是工作,我正在处理Reader部分内部的if else条件可能出现的错误。下面是代码:

public static Tuple<int, string> GetIDAndString(string term)
{
    try
    {
        using (SqlConnection con = GetConnection())
        using (cmd = new SqlCommand())
        using (myReader)
        {
            int ID = 0;
            string status = string.Empty;
            cmd.Connection = con;
            con.Open();
            cmd.CommandText = @"SELECT t.TableID, t.Status
                                FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */
                                WHERE t.Term = @term";
            cmd.Parameters.AddWithValue("@term", term);
            myReader = cmd.ExecuteReader();
            while(myReader.Read())
            {
                ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0);
                status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();
            }
            myReader.Close();
            return new Tuple<int, string>(ID, status);
        }
    }
    catch (Exception)
    {
        throw;
    }
}

我知道我应该使用一个类而不是Tuple,但我不能改变现有的代码,正如你所看到的。所以主要的问题是,在生产服务器中,有一个Index out of bounds array exception在该方法中,但我不能确定是什么问题。

即使在查询中没有找到该术语,myReader也不会进入,并且我将返回ID = 0, status = string.Empty。有时,当我调试代码和工作在develpment server,我的代码开始崩溃无处不在,显示我的异常是测试代码,我必须重新打开解决方案,以避免(我还没有找到一个解决方案,甚至没有清理解决方案)。

所以我希望有人在production server中有类似的经验。我没有生产服务器的规格,所以我对服务器一无所知。

未同步的代码抛出索引超出数组的边界

首先,您不需要try/catch块,您不用它做任何事情。之后不要在课堂上分享SqlDataReader,这可能会带来问题,问题可能就来自于此。在您的while中,您一直覆盖IDStatus的值。也许一个好主意是在您的查询上调用Top 1并通过正确的字段对其进行排序。也不需要Dispose() SqlCommand, SqlCommand的构造函数正在调用SupressFinalization()

为什么会发生这个问题:假设您的查询返回1000条记录,其中TableIDStatus列,并且您正在进入while循环。此时,其他一些用户正在进入您的应用程序并执行另一个方法,该方法覆盖SqlDataReader并返回只有一列的5条记录。在while循环的下一次迭代中,您将收到异常。因此,您不应该将整个类的reader定义为static。静态变量在所有应用程序用户之间共享。

public static Tuple<int, string> GetIDAndString(string term)
{
    int ID = 0;
    string status = string.Empty;
    using (SqlConnection con = GetConnection())
    {
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = con;
        con.Open();
        cmd.CommandText = @"SELECT t.TableID, t.Status
                            FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */
                            WHERE t.Term = @term";
        cmd.Parameters.AddWithValue("@term", term);

        using(SqlDataReader myReader = cmd.ExecuteReader())
        {
            while(myReader.Read())
            {
                 ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0);
                 status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();
            }
        }
    }
    return new Tuple<int, string>(ID, status);
}

这可能发生在您执行ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0);status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();时,因为结果集不符合您的期望。您应该在实际阅读读者行之前添加日志记录,这可能有助于您查明问题

我想这个问题是由myReader字段引起的,我认为这是静态的。如果您查看https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx上的SqlDataReader(我认为这是字段的类型)文档,您会发现实例方法不是线程安全的,因此必须同步访问该字段。

using (myReader)捕获读取器当时的值并稍后处理。它不会记住变量。这必须是这样,你可以从这个例子中看到:using (Random() ? myReader : null)。显然,c#语言不会在处理时重新执行该表达式。它只运行一次

所以你在处理一些旧的/其他的阅读器。

如果您在线程之间共享对象(可能使用静态变量),这通常是一个竞争条件。不要那样做。使用当地人。这里没有必要/没有优势使用静态变量