我的工作最近开始使用PetaPoco
,虽然很棒,但我错过了Dapper
允许将单个查询中的多个结果网格处理成 pocos 的功能。
结果,我编写了自己的实现PetaPoco
- 如下所示 - 但是有没有人自己编写并愿意分享它?
我想可能还有其他人错过了这个功能。
我的工作最近开始使用PetaPoco
,虽然很棒,但我错过了Dapper
允许将单个查询中的多个结果网格处理成 pocos 的功能。
结果,我编写了自己的实现PetaPoco
- 如下所示 - 但是有没有人自己编写并愿意分享它?
我想可能还有其他人错过了这个功能。
编辑 (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.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 分支,但我认为如果我将其发布在这里,人们可能会得到一些好处。