【问题标题】:Reader.IsConsumed is false but object was disposedReader.IsConsumed 为 false,但对象已被释放
【发布时间】:2016-02-22 12:54:37
【问题描述】:

我正在使用QueryMultiple,它返回一个GridReader

由于我不知道我要读取多少数据,所以我以IsConsumed 的停止条件循环阅读器:

using (var reader = conn.QueryMultiple(mySql)) {
  while(!reader.IsConsumed) {
    reader.Read<...>
  }
}

但是,我在最后一次阅读时总是收到ObjectDisposedExceptionIsConsumed 的值仍然是false

我尝试将DynamicParameters 传递给查询以获取回调(这似乎通过IParameterCallbacks 很有用),但我无法将它修补在一起。

我真的不想在代码中出现这样的预期异常。感谢您的帮助。

我正在使用 SQL Server,我的提供程序是 .NET 4.5 中的 System.Data.SqlClient,Dapper 版本 1.40.0.0

例如一个失败的测试:

        [TestMethod]
        public void QueryMultipleWithCursor()
        {

            const string sql = @"
DECLARE @CurrentDate DATE
DECLARE DatesCursor CURSOR LOCAL FOR
    SELECT DISTINCT DataDate FROM Data_Table ORDER BY DataDate 
OPEN DatesCursor
FETCH NEXT FROM DatesCursor INTO @CurrentDate

WHILE @@FETCH_STATUS = 0 BEGIN

    SELECT DISTINCT
        DataDate AS Date1,
        DataDate AS Date2
        FROM Data_Table
        WHERE DataDate=@CurrentDate

    FETCH NEXT FROM DatesCursor INTO @CurrentDate
END
CLOSE DatesCursor
DEALLOCATE DatesCursor";

            using (var conn = _database.GetConnection())
            {
                    var reader = conn.QueryMultiple(sql);
                    while (!reader.IsConsumed)
                    {
                            reader.Read<DateTime, DateTime, DateTime>(
                                (date1, date2) => date1,
                                splitOn: "Date2").ToList();
                    }
            }
        }

我得到了一个NullReferenceException,它的堆栈如下:

at Dapper.SqlMapper.GridReader.NextResult() in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 4440
   at Dapper.SqlMapper.GridReader.<MultiReadInternal>d__9`8.System.IDisposable.Dispose()
   at Dapper.SqlMapper.GridReader.<MultiReadInternal>d__9`8.MoveNext() in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 4309
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Dapper.SqlMapper.GridReader.Read[TFirst,TSecond,TReturn](Func`3 func, String splitOn, Boolean buffered) in D:\Dev\dapper-dot-net\Dapper NET40\SqlMapper.cs:line 4330
   at Project.MyTests.QueryMultipleWithCursor() in C:\Project\MyTests.cs:line 171
Result Message: 
Test method Project.MyTests.QueryMultipleWithCursor threw exception: 
System.NullReferenceException: Object reference not set to an instance of an object.

【问题讨论】:

  • 重新编辑:我使用 .NET 4.5、Dapper 1.40.0、SqlClient 设置了一个测试平台,一切正常pastie.org/10733819 - 我很乐意尝试并提供帮助 - 如果有错误:修复它。但我无法重现该问题。你能帮我举一个不符合你描述方式的例子吗?
  • 你能在相同的配置和测试上尝试像 Marc 这样的独立测试吗?我想知道在多个上下文中使用相同的连接对象,比如并行任务,你可以在其中获得相同 GridReader 的引用,其中一个读取和关闭,因此消耗和其他抛出对象处理异常
  • 我添加了一个失败的单元测试。我无法通过足够简单的测试重现ObjectDisposedException,但我已经非常接近了。我知道测试仍然很复杂,但我不明白为什么它不应该工作。

标签: dapper idisposable sqldatareader objectdisposedexception


【解决方案1】:

我已经推送了以下内容,它通过 SQL Server / SqlConnection;所以它可以工作

[Fact]
public void SO35554284_QueryMultipleUntilConsumed()
{
    using (var reader = connection.QueryMultiple(
        "select 1 as Id; select 2 as Id; select 3 as Id;"))
    {
        List<HazNameId> items = new List<HazNameId>();
        while (!reader.IsConsumed)
        {
            items.AddRange(reader.Read<HazNameId>());
        }
        items.Count.IsEqualTo(3);
        items[0].Id.IsEqualTo(1);
        items[1].Id.IsEqualTo(2);
        items[2].Id.IsEqualTo(3);
    }
}

我想知道这里的问题是否与特定的 ADO.NET 提供程序有关。您可能需要准确指定:

  • 您正在使用什么后端 RDBMS / 等(SQL Server?Oracle?Postgresql?...?)
  • 您使用的是什么 ADO.NET 提供程序
  • 您正在使用什么运行时(.NET what.what?core-clr?)/操作系统
  • 您使用的具体库版本(以上是针对源代码,与 1.50.0-beta8 最相似)

【讨论】:

  • @Marc Gravell 没有尝试过,但是对于多个 Select 绝对没有记录,这会如何表现,在当前测试中将有一条记录可供使用
  • @MrinalKamboj 好问题;稍后我将更改测试以进行 0、1、2、3、... 等选择
【解决方案2】:

我在使用 Dapper 时遇到了同样的问题,我使用版本 1.42.0 和 SQL Server 2012 作为后端。经过调试,我发现只有当我们尝试在最后一个结果集上使用 Dapper 的 splitOn 选项创建多个对象时才会出现此问题。

我在 GitHub 上提交了一个新问题 https://github.com/StackExchange/dapper-dot-net/issues/469

【讨论】:

  • 我认为这更适合作为评论,因为它显然不是解决问题的方法。无论如何,欢迎来到 SO!
  • 我意识到了,但“添加评论”链接已禁用,我无法将其添加为评论。实际上它说,“你必须有 50 声望才能发表评论”
  • 实际上将其识别为错误可能是我能得到的最好的结果
【解决方案3】:

这似乎是 Dapper 实现的一个问题,同时我同时使用 DapperSqlDataReader,这更可靠:

public static SqlMapper.GridReader QueryMultipleStoredProcedure(this IDbConnection dbConnection, string spName, object parameters, out SqlDataReader sqlDataReader)
        {
            var gridReader = dbConnection.QueryMultiple(spName, new DynamicParameters(parameters), commandType: CommandType.StoredProcedure);
            sqlDataReader = typeof (SqlMapper.GridReader).GetInstanceField<SqlDataReader>(gridReader, "reader");
            return gridReader;
        }

        private static T GetInstanceField<T>(this Type type, object instance, string fieldName)
        {
            var bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            var field = type.GetField(fieldName, bindFlags);
            return (T) field?.GetValue(instance);
        }

然后我可以使用sqlDataReader.HasRows

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-31
    相关资源
    最近更新 更多