Dapper尝试使用"QueryMultiple"时出现"尝试在读取器关闭时调用FieldCount是无效的"错误。

8

我有一个Dapper.NET的QueryMultiple方法的包装器方法。它可以成功地从一个存储过程中获取数据,该过程有三个查询,所有查询都是SELECT查询。但是在获取数据后,我无法使用ReadReadAsync将数据分配给类变量。以下是我的代码。

public Tuple<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>> 
        QueryMultiple<T1, T2, T3>()
    {
        try
        {
            var data = MultiQuery("[App].[USP_GetAllCategories]");
            var category = data.Read<T1>();
            var subcategory = data.Read<T2>();
            var subSubcategory = data.Read<T3>();
            return new Tuple<IEnumerable<T1>, IEnumerable<T2>, IEnumerable<T3>>(
                category, subcategory, subSubcategory);
        }
        catch (Exception)
        {
            return null;
        }
    }

    public SqlMapper.GridReader MultiQuery(string storedProcedureName)
    {
        using (var connection = LocalConnection())
        {
            try
            {
                return connection.QueryMultiple(
                    sql: storedProcedureName,
                    commandType: CommandType.StoredProcedure);
            }
            catch (Exception)
            {
                return null;
            }
            finally
            {
                CloseConnection(connection);
            }
        }
    }

1
在调用Read之前,您正在释放连接。我不确定dapper是否立即加载数据,但无论如何,代码看起来都是错误的。实际上没有一个很好的理由去编写一个仅仅包装using的方法。只需将using块放在QueryMultiple中即可。 - Rob
1
@Rob 参考:不,它不会(至少在这种情况下不会);它基本上是包装了一个打开的 IDataReader - Marc Gravell
1
@Rob 我原以为在释放连接后,我仍然可以使用GridReader来读取数据。但是正如你所看到的,它似乎不起作用。我创建了MultiQuery,这样其他开发人员就不需要知道如何查询,只需使用GridReader获取数据。 - haider_kazal
1
@haider_kazal没有任何保证,一个读取器在关闭连接后仍然有效;它是一个“开放式”读取器-我们还没有实际从TDS流中使用任何数据。 - Marc Gravell
好的...谢谢@MarcGravell - haider_kazal
1个回答

8

请看你的MultiQuery方法。特别是,看看finally块。现在考虑:在数据被消耗之前,该块已被调用。基本上,不要这样做。

如果是我:

using (var connection = LocalConnection())
uaing (var data = conn.QueryMultiple("[App].[USP_GetAllCategories]",
    command type: CommandType.StoredProcedure))
{
    //... Consume
}

如果编写一个 QueryMultipleSP 扩展方法能够增加命令类型的可读性,那么可以考虑这样做,但是不要......

嘿,谢谢你的回答。 然而问题是,我需要编写MultiQuery函数,以便开发人员不需要了解数据库细节,只需从中获取GridReaderRead数据。我想这可能是不可能的。 - haider_kazal
如果你想返回一个读取器,你需要让客户端代码负责释放读取器和连接。你需要在API文档中正确记录这一点。而且,你不能在IDisposables上使用"using"关键字。你可以通过在类本身实现IDisposable接口,并在Dispose方法中释放依赖项来实现你想要的效果。但是,我认为这是一个API设计的问题。 - Erik A. Brandstadmoen
1
@haider_kazal,您可以这样做,但需要将 GridReader 进行包装,使其成为读取器的职责关闭/处理连接并处理包装的 GridReader - Marc Gravell
2
你可以将MultiQuery封装在一个带有回调参数的包装器函数中:Func<SqlMapper.GridReader,T> callback。这个包装器函数将在using块内返回对回调函数的调用。它确保连接始终被正确关闭/释放,客户端代码可以根据需要处理GridReader对象。 - goPlayerJuggler
@goPlayerJuggler的回答是做这件事最正确的方法。我花了一些时间才找到它,但它保留了原帖所要求的内容。这是一种无缝的方式,让开发人员在不涉及SQL连接的情况下进行查询。 - Craig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接