PetaPoco - 多结果集支持

Posted

技术标签:

【中文标题】PetaPoco - 多结果集支持【英文标题】:PetaPoco - Multiple result set support 【发布时间】:2013-11-03 18:38:15 【问题描述】:

我的工作最近开始使用PetaPoco,虽然很棒,但我错过了Dapper 允许multiple result grids from a single query to be processed 进入pocos 的功能。

因此,我为 PetaPoco 编写了自己的实现 - 如下所示 - 但有没有人自己编写并愿意分享它?

我认为可能还有其他人错过了此功能。

【问题讨论】:

关于接近投票,如果您让我知道不清楚的地方,我很乐意添加更多细节。 【参考方案1】:

编辑(2016-06-27): 从 PetaPoco 的 5.1.181 版本开始(在撰写本文时为测试版),此功能现已在 PetaPoco NuGet 包中提供

信息

我使用 Dapper 源作为实现此功能的灵感,因此任何相似之处都是由于此。

这会直接更改 PetaPoco.cs 文件,从一开始的评论来看,您可能不应该这样做!

此代码已经过测试但不彻底,请谨慎行事!

用法

private string _sql = @"SELECT * FROM WebOrders w INNER JOIN Address a ON a.Id = w.DeliveryAddressId WHERE OrderId = @0
                        SELECT * FROM WebOrderLines WHERE OrderId = @0 AND CustomerId = @1
                        SELECT * FROM ContactDetails WHERE CustomerId = @1";

private readonly Database _db = new Database("Shop");

var result = new WebOrder();

using (var multi = _db.QueryMultiple(_sql, 12345, 67890))

    result = multi.Read<WebOrder, Address, WebOrder>((w, a) =>  w.Address = a; return w; ).Single();
    result.OrderLines = multi.Read<WebOrderLines>().ToList();
    result.ContactDetails = multi.Read<ContactDetails>().ToList();


return result;

更改 - PetaPoco (Core) v.5.0.1

在类PetaPoco.Database内添加:

    #region operation: Multi-Result Set
    /// <summary>
    /// Perform a multi-results set query
    /// </summary>
    /// <param name="sql">An SQL builder object representing the query and it's arguments</param>
    /// <returns>A GridReader to be queried</returns>
    public GridReader QueryMultiple(Sql sql)
    
        return QueryMultiple(sql.SQL, sql.Arguments);
    

    /// <summary>
    /// Perform a multi-results set query
    /// </summary>
    /// <param name="sql">The SQL query to be executed</param>
    /// <param name="args">Arguments to any embedded parameters in the SQL</param>
    /// <returns>A GridReader to be queried</returns>
    public GridReader QueryMultiple(string sql, params object[] args)
    
        OpenSharedConnection();

        GridReader result = null;

        var cmd = CreateCommand(_sharedConnection, sql, args);

        try
        
            var reader = cmd.ExecuteReader();
            result = new GridReader(this, cmd, reader);
        
        catch (Exception x)
        
            if (OnException(x))
                throw;
        
        return result;
    
    #endregion

在根级别(命名空间:PetaPoco)添加:

#region Multi-Results Set GridReader
public class GridReader : IDisposable

    private IDataReader _reader;
    private IDbCommand _command;
    private readonly Database _db;

    /// <summary>
    /// The control structure for a multi-result set query
    /// </summary>
    /// <param name="database"></param>
    /// <param name="command"></param>
    /// <param name="reader"></param>
    internal GridReader(Database database, IDbCommand command, IDataReader reader)
    
        _db = database;
        _command = command;
        _reader = reader;
    

    #region public Read<T> methods

    /// <summary>
    /// Reads from a GridReader, returning the results as an IEnumerable collection
    /// </summary>
    /// <typeparam name="T">The Type representing a row in the result set</typeparam>
    /// <returns>An enumerable collection of result records</returns>
    public IEnumerable<T> Read<T>()
    
        return SinglePocoFromIDataReader<T>(_gridIndex);
    
    /// <summary>
    /// Perform a multi-poco read from a GridReader
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<T1> Read<T1, T2>()
    
        return MultiPocoFromIDataReader<T1>(_gridIndex, new Type[] typeof (T1), typeof (T2), null);
    
    /// <summary>
    /// Perform a multi-poco read from a GridReader
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <typeparam name="T3">The third POCO type</typeparam>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<T1> Read<T1, T2, T3>()
    
        return MultiPocoFromIDataReader<T1>(_gridIndex, new Type[] typeof (T1), typeof (T2), typeof (T3), null);
    
    /// <summary>
    /// Perform a multi-poco read from a GridReader
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <typeparam name="T3">The third POCO type</typeparam>
    /// <typeparam name="T4">The forth POCO type</typeparam>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<T1> Read<T1, T2, T3, T4>()
    
        return MultiPocoFromIDataReader<T1>(_gridIndex,
                                            new Type[] typeof (T1), typeof (T2), typeof (T3), typeof (T4), null);
    
    /// <summary>
    /// Perform a multi-poco query
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <typeparam name="TRet">The type of objects in the returned IEnumerable</typeparam>
    /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships</param>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<TRet> Read<T1, T2, TRet>(Func<T1, T2, TRet> cb)
    
        return MultiPocoFromIDataReader<TRet>(_gridIndex, new Type[] typeof (T1), typeof (T2), cb);
    
    /// <summary>
    /// Perform a multi-poco query
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <typeparam name="T3">The third POCO type</typeparam>
    /// <typeparam name="TRet">The type of objects in the returned IEnumerable</typeparam>
    /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships</param>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<TRet> Read<T1, T2, T3, TRet>(Func<T1, T2, T3, TRet> cb)
    
        return MultiPocoFromIDataReader<TRet>(_gridIndex, new Type[] typeof (T1), typeof (T2), typeof (T3), cb);
    
    /// <summary>
    /// Perform a multi-poco query
    /// </summary>
    /// <typeparam name="T1">The first POCO type</typeparam>
    /// <typeparam name="T2">The second POCO type</typeparam>
    /// <typeparam name="T3">The third POCO type</typeparam>
    /// <typeparam name="T4">The forth POCO type</typeparam>
    /// <typeparam name="TRet">The type of objects in the returned IEnumerable</typeparam>
    /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships</param>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    public IEnumerable<TRet> Read<T1, T2, T3, T4, TRet>(Func<T1, T2, T3, T4, TRet> cb)
    
        return MultiPocoFromIDataReader<TRet>(_gridIndex,
                                              new Type[] typeof (T1), typeof (T2), typeof (T3), typeof (T4), cb);
    

    #endregion

    #region PocoFromIDataReader

    /// <summary>
    /// Read data to a single poco
    /// </summary>
    /// <typeparam name="T">The type representing a row in the result set</typeparam>
    /// <param name="index">Reader row to be read from the underlying IDataReader</param>
    /// <returns></returns>
    private IEnumerable<T> SinglePocoFromIDataReader<T>(int index)
    
        if (_reader == null)
            throw new ObjectDisposedException(GetType().FullName, "The data reader has been disposed");
        if (_consumed)
            throw new InvalidOperationException(
                "Query results must be consumed in the correct order, and each result can only be consumed once");
        _consumed = true;

        var pd = PocoData.ForType(typeof (T));
        try
        
            while (index == _gridIndex)
            
                var factory =
                    pd.GetFactory(_command.CommandText, _command.Connection.ConnectionString, 0, _reader.FieldCount,
                                  _reader) as Func<IDataReader, T>;

                while (true)
                
                    T poco;
                    try
                    
                        if (!_reader.Read())
                            yield break;
                        poco = factory(_reader);
                    
                    catch (Exception x)
                    
                        if (_db.OnException(x))
                            throw;
                        yield break;
                    

                    yield return poco;
                
            
        
        finally // finally so that First etc progresses things even when multiple rows
        
            if (index == _gridIndex)
            
                NextResult();
            
        
    

    /// <summary>
    /// Read data to multiple pocos
    /// </summary>
    /// <typeparam name="TRet">The type of objects in the returned IEnumerable</typeparam>
    /// <param name="index">Reader row to be read from the underlying IDataReader</param>
    /// <param name="types">An array of Types representing the POCO types of the returned result set.</param>
    /// <param name="cb">A callback function to connect the POCO instances, or null to automatically guess the relationships</param>
    /// <returns>A collection of POCO's as an IEnumerable</returns>
    private IEnumerable<TRet> MultiPocoFromIDataReader<TRet>(int index, Type[] types, object cb)
    
        if (_reader == null)
            throw new ObjectDisposedException(GetType().FullName, "The data reader has been disposed");
        if (_consumed)
            throw new InvalidOperationException(
                "Query results must be consumed in the correct order, and each result can only be consumed once");
        _consumed = true;

        try
        
            var cmd = _command;
            var r = _reader;

            var factory = MultiPocoFactory.GetFactory<TRet>(types, cmd.Connection.ConnectionString, cmd.CommandText,
                                                            r);
            if (cb == null)
                cb = MultiPocoFactory.GetAutoMapper(types.ToArray());
            bool bNeedTerminator = false;

            while (true)
            
                TRet poco;
                try
                
                    if (!r.Read())
                        break;
                    poco = factory(r, cb);
                
                catch (Exception x)
                
                    if (_db.OnException(x))
                        throw;
                    yield break;
                

                if (poco != null)
                    yield return poco;
                else
                    bNeedTerminator = true;
            
            if (bNeedTerminator)
            
                var poco = (TRet) (cb as Delegate).DynamicInvoke(new object[types.Length]);
                if (poco != null)
                    yield return poco;
                else
                    yield break;
            
        
        finally
        
            if (index == _gridIndex)
            
                NextResult();
            
        
    

    #endregion

    #region DataReader Management

    private int _gridIndex;
    private bool _consumed;

    /// <summary>
    /// Advance the IDataReader to the NextResult, if available
    /// </summary>
    private void NextResult()
    
        if (!_reader.NextResult()) return;
        _gridIndex++;
        _consumed = false;
    

    /// <summary>
    /// Dispose the grid, closing and disposing both the underlying reader, command and shared connection
    /// </summary>
    public void Dispose()
    
        if (_reader != null)
        
            if (!_reader.IsClosed && _command != null) _command.Cancel();
            _reader.Dispose();
            _reader = null;
        

        if (_command != null)
        
            _command.Dispose();
            _command = null;
        
        _db.CloseSharedConnection();
    

    #endregion

#endregion

我已将此更改提交给 PetaPoco v5 branch,但我认为如果我将其发布在这里,人们可能会从中受益。

【讨论】:

谢谢,对我有用。 我已返回动态列名称,例如 2017.01.13。我使用 QueryMultiple 得到了结果,因为它从我的 SP 返回两个表。当我更改日期并获取更新记录但列名未更改时。它与第一个请求相同。所以请告诉我如何解决这个问题。 @VijayMaheriya 请问这是一个独立的问题

以上是关于PetaPoco - 多结果集支持的主要内容,如果未能解决你的问题,请参考以下文章

redshift 返回的结果集是不是支持重置迭代器?

MyBatis结果集一对多映射

oracle PLSQL 多结果集嵌套循环处理优化

mybatis返回多结果集

错题集

如何检查结果集是不是有一行或更多?