【问题标题】:SqlDataReader Throws "Invalid attempt to read when no data is present" but OleDbReader does notSqlDataReader 抛出“不存在数据时尝试读取无效”但 OleDbReader 没有
【发布时间】:2017-12-24 17:10:07
【问题描述】:

我使用 Microsoft SQL Server 迁移助手将 Access 数据库迁移到 SQL。

现在,我无法读取数据。

return reader.GetInt32(0); 在检索到 32 行后抛出 Invalid attempt to read when no data is present 异常。如果我将CommandBehavior.SequentialAccess 添加到我的命令中,我可以读取 265 行,每次

数据

查询(在 SQL Management Studio 中):

SELECT *
FROM Products2
WHERE Products2.PgId = 337;

第 32 行没有什么特别之处,如果我颠倒顺序,仍然是第 32 行杀死它。

第 265 行,仅供参考。

代码

查询:

SELECT *
FROM Products2
WHERE Products2.PgId = @productGroupId;

参数:

Name = "productGroupId"
Value = 337

执行:

public async Task ExecuteAsync(string query, Action<IDataReader> onExecute, params DataParameter[] parameters)
{
    using(var command = builder.BuildCommand(query))
    {
        foreach(var parameter in parameters)
            command.AddParameter(parameter);
        if(!connection.IsOpen)
            connection.Open();
        await Task.Run(async () =>
        {
            using(var reader = await command.ExecuteAsync())
                if(await reader.ReadAsync())
                    onExecute(reader);
        });
    }
}

阅读:

await executor.ExecuteAsync(query, async reader =>
{
    do
    {
        products.Add(GetProduct(reader));
        columnValues.Enqueue(GetColumnValues(reader).ToList());
    } while(await reader.ReadAsync());
}, parameters.ToArray());

await reader.ReadAsync() 返回 true,但是当 GetProduct(reader) 第 32 次调用 reader.GetInt32(0); 时,它会抛出异常。

如果数据少于 32 行,则可以正常工作,如果是 SequentialAccess,则为 265。 我尝试增加CommandTimeout,但没有帮助。当我再次将连接交换到OleDb 时,它工作得很好。

提前致谢。


编辑

如果我只用几个特定列替换查询中的*,它就可以工作。当我阅读 ~12 列时,它失败了,但晚于第 32 行。

根据要求,GetProduct:

private Product GetProduct(IDataReader productReader)
{
    return new Product
    {
        Id = productReader.ReadLong(0),
        Number = productReader.ReadString(2),
        EanNo = productReader.ReadString(3),
        Frequency = productReader.ReadInt(4),
        Status = productReader.ReadInt(5),
        NameId = productReader.ReadLong(6),
        KMPI = productReader.ReadByte(7),
        OEM = productReader.ReadString(8),
        CurvesetId = productReader.ReadInt(9),
        HasServiceInfo = Convert.ToBoolean(productReader.ReadByte(10)),
        ColumnData = new List<ColumnData>()
    };
}

获取列数据:

private IEnumerable<long> GetColumnValues(IDataReader productReader)
{
    var columnValues = new List<long>();
    int columnIndex = 11;
    while(!productReader.IsNull(columnIndex))
        columnValues.Add(productReader.ReadLong(columnIndex++));
    return columnValues;
}

还有适配器:

public long ReadLong(int columnIndex)
{
    return reader.GetInt32(columnIndex);
}

好的,时间越来越长了。 :) 感谢@Igor,我尝试创建一个小的工作示例。这似乎工作正常:

    private static async Task Run()
    {
        var result = new List<Product>();
        string conString = @" ... ";
        var con = new SqlConnection(conString);
        con.Open();
        using(var command = new SqlCommand("SELECT * FROM Products2 WHERE Products2.PgId = @p;", con))
        {
            command.Parameters.Add(new SqlParameter("p", 337));
            await Task.Run(async () =>
            {
                using(var productReader = await command.ExecuteReaderAsync())
                    while(await productReader.ReadAsync())
                    {
                        result.Add(new Product
                        {
                            Id = productReader.GetInt32(0),
                            Number = productReader.GetString(2),
                            EanNo = productReader.GetString(3),
                            Frequency = productReader.GetInt16(4),
                            Status = productReader.GetInt16(5),
                            NameId = productReader.GetInt32(6),
                            KMPI = productReader.GetByte(7),
                            OEM = productReader.GetString(8),
                            CurvesetId = productReader.GetInt16(9),
                            HasServiceInfo = Convert.ToBoolean(productReader.GetByte(10))
                        });
                        GetColumnValues(productReader);
                    }
            });
        }
        Console.WriteLine("End");
    }
    private static IEnumerable<long> GetColumnValues(SqlDataReader productReader)
    {
        var columnValues = new List<long>();
        int columnIndex = 11;
        while(!productReader.IsDBNull(columnIndex))
            columnValues.Add(productReader.GetInt32(columnIndex++));
        return columnValues;
    }
}

这是 Access 中的数据,以防万一:

【问题讨论】:

  • 不要异步,你的问题可能会消失。
  • @BensaysNotoPoliticsonSO,你建议我如何让它保持响应?
  • 通过正确索引它以便快速查询。
  • 我无法控制数据库的原理图。
  • 至于最后一列,您是说一旦遇到null /DBNull.Value,那么所有后续列的值也将为空?如果不是这种情况,那么这将是一个可能的失败点。

标签: c# sql ms-access oledb


【解决方案1】:

我设法解决了这个问题。

但我仍有疑问,因为我不明白为什么这不会影响 Access。

我变了:

public async Task ExecuteAsync(string query, Action<IDataReader> onExecute, params DataParameter[] parameters)
{
    using(var command = builder.BuildCommand(query))
    {
        foreach(var parameter in parameters)
            command.AddParameter(parameter);
        if(!connection.IsOpen)
            connection.Open();
        await Task.Run(async () =>
        {
            using(var reader = await command.ExecuteAsync())
                if(await reader.ReadAsync())
                    onExecute(reader);
        });
    }
}

收件人:

public async Task ExecuteAsync(string query, Func<IDataReader, Task> onExecute, params DataParameter[] parameters)
{
    using(var command = builder.BuildCommand(query))
    {
        foreach(var parameter in parameters)
            command.AddParameter(parameter);
        if(!connection.IsOpen)
            connection.Open();
        using(var reader = await command.ExecuteAsync())
            await onExecute(reader);
    }
}

如果有人能解释为什么这会有所帮助,我将不胜感激。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    • 2010-11-11
    • 1970-01-01
    相关资源
    最近更新 更多