/// <summary> /// Gets a page of size pageSize items, inside a db from dbFactory, and passes that to foreachBody(page, db). /// /// Then closes the DbContext to keep EF from freaking out on long jobs, and does the above steps again. /// /// Turns out to be the best way to handle processing a lot of items in EF. As an extra tweak, set a dirty flag at the top of the /// loop body you pass in, flag it when you change anything, and call .SaveChanges() just the once as the loop body is ending if /// the flag is set. /// </summary> /// <typeparam name="TDb"></typeparam> /// <typeparam name="TModel"></typeparam> /// <param name="pageSize"></param> /// <param name="dbFactory"></param> /// <param name="query"></param> /// <param name="foreachBody">The body you'd normally place in a foreach loop for pages of items.</param> public static void PagesOf <TDb, TModel>(int pageSize, BaseDbFactory <TDb> dbFactory, Func <TDb, IOrderedQueryable <TModel> > query, Action <IEnumerable <TModel>, TDb> foreachBody) where TDb : DbContext, new() { int length = pageSize; for (int page = 0; length == pageSize; page++) { using (var db = dbFactory.NewDb()) { var items = query(db).Skip(page * pageSize).Take(pageSize).ToArray(); foreachBody(items, db); length = items.Length; } } }
/// <summary> /// Pages through items, and detaches from the DbContext for each page; the body of your foreach loop will execute detached, meaning /// no lazy loading or other EF access is possible - only the detached objects as-is are readable (no virtual navigation properties) /// and no db is available for actions like .SaveChanges(); /// </summary> /// <typeparam name="TDb"></typeparam> /// <typeparam name="TModel"></typeparam> /// <param name="pageSize"></param> /// <param name="dbFactory"></param> /// <param name="query"></param> /// <returns></returns> public static IEnumerable <TModel> EnumeratePagedDetached <TDb, TModel>(int pageSize, BaseDbFactory <TDb> dbFactory, Func <TDb, IOrderedQueryable <TModel> > query) where TDb : DbContext, new() { int length = pageSize; for (int page = 0; length == pageSize; page++) { TModel[] items; using (var db = dbFactory.NewDb()) { items = query(db).Skip(page * pageSize).Take(pageSize).ToArray(); } foreach (var item in items) { yield return(item); } length = items.Length; } }