SqlDataReader索引超出范围返回正确的列计数时的异常

时间:2016-08-02 20:51:51

标签: c# asp.net .net sql-server tsql

我有一个存储过程可以返回正确的列数,但下面的代码大部分时间都有效,但 RANDOMLY 会抛出异常。我们最近升级到.NET 4.6,之后我们注意到了异常。

问题:

1异常发生的地点和原因在哪里?

2根据底部的源代码,SQL客户端如何从服务器端接收空元数据?

存储过程GetUser

CREATE PROCEDURE [dbo].[GetUser]    
    @UserID int
AS
BEGIN

    SET NOCOUNT ON;

    DECLARE @UserIDChar NVARCHAR(255);
    SET @UserIDChar = convert(nvarchar(255), @UserID);

    SELECT TOP 1
        A.Value1 As FirstName, 
        A.Value2 As LastName
       -- three more columns omitted here
    FROM dbo.Activity as A      
    WHERE A.ActivityValue = @UserIDChar  --ActivityValue is NVARCHAR(255) type
    ORDER BY A.DateCreated DESC 

    SET NOCOUNT OFF;

END 

C#网络层:

using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
{
    SqlCommand cmd = new SqlCommand();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "GetUser";  //the proc returns one row that consists of two columns
    cmd.Connection = cn;
    cmd.Parameters.AddWithValue("@UserID", userId);

    cn.Open();

    using (IDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleResult))
    {
        if (dr.Read())  //check if row is available
        {
            string firstName = (string)dr[0];
            string lastName = (string)dr[1];                        
            // three more columns omitted here
            return firstName + " " + lastName;
        }
    }
}

错误:

  

异常类型:System.IndexOutOfRangeException
  消息:索引超出了数组的范围   数据:System.Collections.ListDictionaryInternal
  TargetSite:Void CheckDataIsReady(Int32,Boolean,Boolean,System.String)
  来源:System.Data

以下源代码:

private void CheckDataIsReady(int columnIndex, bool allowPartiallyReadColumn = false, bool permitAsync = false, string methodName = null) {
            if (_isClosed) {
                throw ADP.DataReaderClosed(methodName ?? "CheckDataIsReady");
            }
            if ((!permitAsync) && (_currentTask != null)) {
                throw ADP.AsyncOperationPending();
            }
            Debug.Assert(!_sharedState._dataReady || _metaData != null, "Data is ready, but there is no metadata?");
            if ((!_sharedState._dataReady) || (_metaData == null)) {
                throw SQL.InvalidRead();
            }
            if ((columnIndex < 0) || (columnIndex >= _metaData.Length)) {
                throw ADP.IndexOutOfRange();
            }
            if ((IsCommandBehavior(CommandBehavior.SequentialAccess)) &&                                    // Only for sequential access
                ((_sharedState._nextColumnDataToRead > columnIndex) || (_lastColumnWithDataChunkRead > columnIndex) ||   // Read past column
                ((!allowPartiallyReadColumn) && (_lastColumnWithDataChunkRead == columnIndex)) ||           // Partially read column
                ((allowPartiallyReadColumn) && (HasActiveStreamOrTextReaderOnColumn(columnIndex))))) {      // Has a Stream or TextReader on a partially-read column
                    throw ADP.NonSequentialColumnAccess(columnIndex, Math.Max(_sharedState._nextColumnDataToRead, _lastColumnWithDataChunkRead + 1));
            }
        }

http://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlDataReader.cs,577d642dce99ed0d

4 个答案:

答案 0 :(得分:0)

缺少模式名称可能是混乱的。和你在一起。

另外,下面的C#代码中的一些调试技巧。

  1. 提供存储过程的模式名称。

    IF EXISTS (
                SELECT * FROM INFORMATION_SCHEMA.ROUTINES
                WHERE ROUTINE_TYPE = N'PROCEDURE' and ROUTINE_SCHEMA = N'dbo' and ROUTINE_NAME = N'GetUser'  
            )   
    BEGIN
        DROP PROCEDURE [dbo].[GetUser]
    END
    
    GO
    
    CREATE PROCEDURE dbo.GetUser()
    
    AS 
    
    SELECT TOP 1
        FirstName, LastName
    FROM 
        User
    --omitted WHERE userid = @id
    
    
    GO
    
  2. 将您的c#更改为:

       using (var cn = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString))
      {
           SqlCommand cmd = new SqlCommand();
           cmd.CommandType = CommandType.StoredProcedure;
           cmd.CommandText = "dbo.GetUser";  
           cmd.Connection = cn;
    
           cn.Open();
    
           using (IDataReader dr = cmd.ExecuteReader())
           {
           while (dr.Read()) 
                               string temp = string.Empty;
    
        int fc = dr.FieldCount;    
        if (fc>0)
        {
                        object val2 = dr[0];
                        temp = val1.GetType().ToString();
        }
    
        if (fc>1)
        {
                        object val2 = dr[1];
                        temp = val2.GetType().ToString();
        }
    
    
    
        temp = "did I get here?";
    
        string firstName = (string)dr[0];
        string lastName = (string)dr[1];                        
    
        return firstName + " " + lastName;
    }
           }
           }
    

答案 1 :(得分:0)

我认为这一行会让你陷入困境:

A.Value2 Ad LastName

应该是: A.Value2作为LastName

答案 2 :(得分:0)

您尚未在C#代码中指定参数userid,请使用CommandParameter然后尝试

答案 3 :(得分:0)

试一试 可能无法修复,但你会获得信息,至少它会优雅地失败

try
{ 
    cn.Open();
    string firstName = "not";
    string lastName = "found";
    using (SQLDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleResult))
    {
        if (dr.Read())  //if there is a row, there are two columns. Thus index is used below
        {
            Debug.WriteLine(dr.FieldCount);
            firstName = rdr.IsDBNull(0) ? string.Empty : rdr.GetString(0);
            lastName = rdr.IsDBNull(1) ? string.Empty : rdr.GetString(1);                         
        }
    }
} 
catch (SqlException ex)
{
    Debug.WriteLine("GetUser " + Environment.NewLine + ex.Message);
}
catch (Exception ex)
{
    Debug.WriteLine("GetUser" + Environment.NewLine + ex.Message);
    throw ex;
}
finally
{
    cn.Close();
}
return firstName + " " + lastName;