将多个数据读取器合二为一

时间:2017-05-24 01:06:53

标签: c# sql ado.net

我有一些代码(C#/ ADO.NET),我得到2个或更多读者( IDataReader 实例) - 每个读者都可以是多个数据集的读者,这些数据集意味着通过< strong> NextResult API。

我的任务是将这些内容组合到一个阅读器中并将其返回给我的调用者,以便他们可以通过这个单独的阅读器枚举所有不同的结果 - 根据需要调用 NextResult

(请注意,每个数据集都有不同类型的数据。)。

似乎是一个有效的用例。应该有办法做到这一点吗?

1 个答案:

答案 0 :(得分:0)

为了好玩,我尝试在下面创建一个类。这肯定是一个麻烦的测试。

在解释为什么会有痛苦之前,我会为你提供一个为自己省事的借口。如果您的班级正在创建IDataReader,则可能没有充分的理由将其传递给来电者。您只需从中读取数据并将 传递给调用者即可。有没有什么好理由让呼叫者需要读者而不仅仅是实际数据?打开一个datareader并关闭它是你想要完成的,而不需要在这个过程中获得太多的手,所以如果你可以打开它,得到你需要的东西,然后关闭它,这是理想的。

当我们在IDataReader内从一个结果集前进到下一个结果集时,通常是在进行单个调用的情况下,因此更容易理解结果集。我们只调用了过程XYZ,它返回两个结果集,因此我们必须检查两个结果集。我不想处理具有大量结果集的IDataReader,特别是当它是一堆人为组合的较小的结果集时。您必须跟踪大量结果集,并在不同的方法之间切换,因为它们包含不同的列。

还有那些开放式连接的问题。我们通常在与读者完成关系后关闭连接。但现在,当这种联系被关闭时,它就不那么清楚了。你怎么知道哪个连接属于哪个读卡器?如果在不同的阅读器仍在使用时关闭阅读器的连接怎么办?

所以,这里大致了解它的外观。我显然没有测试过这个。如果调用NextResult并且当前阅读器中没有下一个结果,则必须跟踪哪一个是最新的并处理前进到下一个阅读器。而且你必须关闭读者并确保他们都被处理掉了。这可以测试,但只是测试会很头疼,这通常是一个很好的警告,不要做某事。

public class AggregateDataReader : IDataReader
{
    private readonly Queue<IDataReader> _readers;
    private IDataReader _current;

    public AggregateDataReader(IEnumerable<IDataReader> readers)
    {
        _readers = new Queue<IDataReader>(readers);
    }

    private bool AdvanceToNextReader()
    {
        _current?.Dispose();
        var moreReaders = _readers.Any();
        if (moreReaders) _current = _readers.Dequeue();
        return moreReaders;
    }

    public bool NextResult()
    {
        if (_current == null) return false;
        if (_current.NextResult()) return true;
        return AdvanceToNextReader();
    }

    public bool Read()
    {
        return _current.Read();
    }

    public void Dispose()
    {
        _current?.Dispose();
        while (_readers.Any()) _readers.Dequeue().Dispose();
    }

    public string GetName(int i)
    {
        return _current.GetName(i);
    }

  ... lots of these...

    public byte GetByte(int i)
    {
        return _current.GetByte(i);
    }

    public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
    {
        return _currentGetBytes(i, fieldOffset, buffer, bufferoffset, length);
    }

   ... etc...

    public void Close()
    {
        _current?.Close();
        while (_readers.Any()) _readers.Dequeue().Close();
    }

   ... etc...
}