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 - 多结果集支持的主要内容,如果未能解决你的问题,请参考以下文章