I needed a forward-only entity reader to traverse a long resultset for reporting purposes. Because I need a large prefetch path, I've created a simulated IDataReader using paging. Works good and fast.
Below is how to use it, hope this helps anyone.
Ries
/// Fetches all the entities in the giveing query, using paging. Useful to prevent loading all data at once
/// http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=15146&HighLight=1
var ordersQuery = from o in PPPMetaData.Order
join p in PPPMetaData.VwReportingPreselectOrders on o.OrderId equals p.OrderID
where (p.ArrivalHandlingStationID == handlingStation.StationId || p.DepartureHandlingStationID == handlingStation.StationId)
&& ((p.MostActualArrivalTime >= fromTimeUtc && p.MostActualArrivalTime < beforeTimeUtc)
|| (p.MostActualDepartureTime >= fromTimeUtc && p.MostActualDepartureTime < beforeTimeUtc))
select o;
ordersQuery = ordersQuery.WithPath(DetailsPath());
var orders = new EntityReader<OrderEntity>(this.context, ordersQuery);
foreach (var order in orders)
{
// .. use the order
}
This is the implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SD.LLBLGen.Pro.ORMSupportClasses;
using PPPDataLib.HelperClasses;
namespace PPPBusLib
{
/// <summary>
/// This reads entities one page at a time, to prevent using very much memory for large results set
/// it acts a forward only cursor
/// </summary>
public class EntityReader<T> : PPPBusinessObject, IEnumerable<T> where T : EntityBase2, IEntity2
{
const int DEFAULT_PAGESIZE = 250;
private IQueryable<T> query;
private int pageSize = DEFAULT_PAGESIZE;
public EntityReader(PPPBusinessContext context, IQueryable<T> query) : base(context)
{
this.query = query;
}
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return new EntityReaderEnumerator<T>(context, query, pageSize);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new EntityReaderEnumerator<T>(context, query, pageSize);
}
#endregion
}
public class EntityReaderEnumerator<T> : PPPBusinessObject, IEnumerator<T> where T : EntityBase2, IEntity2
{
private IQueryable<T> query;
private int pageSize;
private int currentPage;
IEnumerator<T> pageEnumerator;
public EntityReaderEnumerator(PPPBusinessContext context, IQueryable<T> query, int pageSize) : base(context)
{
this.query = query;
this.pageSize = pageSize;
this.currentPage = 0;
}
#region IEnumerator<T> Members
public T Current
{
get { return this.pageEnumerator.Current; }
}
#endregion
#region IDisposable Members
public void Dispose()
{
}
#endregion
#region IEnumerator Members
object System.Collections.IEnumerator.Current
{
get { return this.pageEnumerator.Current; }
}
public bool MoveNext()
{
if (this.pageEnumerator == null)
FetchNextPage();
bool hasMore = this.pageEnumerator.MoveNext();
if (!hasMore)
{
FetchNextPage();
hasMore = this.pageEnumerator.MoveNext();
}
return hasMore;
}
public void Reset()
{
this.currentPage = 0;
this.pageEnumerator = null;
}
#endregion
// http://www.llblgen.com/tinyforum/Messages.aspx?ThreadID=15146&HighLight=1
private void FetchNextPage()
{
var pagingQuery = this.query.Skip(this.currentPage * this.pageSize).Take(this.pageSize);
var currentPageData = Execute<EntityCollection<T>>(pagingQuery);
this.pageEnumerator = currentPageData.GetEnumerator();
this.currentPage++;
}
}
}