Refactor GetAllEntries methods for clarity and maintainability

d
This commit is contained in:
Michael Bucari-Tovo 2025-02-26 16:40:15 -07:00
parent 6c5773df24
commit bcbb7610ad
3 changed files with 49 additions and 62 deletions

View File

@ -21,7 +21,7 @@ namespace DataLayer
.AsNoTrackingWithIdentityResolution() .AsNoTrackingWithIdentityResolution()
.GetLibrary() .GetLibrary()
.AsEnumerable() .AsEnumerable()
.Where(lb => !lb.Book.IsEpisodeParent() || includeParents) .Where(c => !c.Book.IsEpisodeParent() || includeParents)
.ToList(); .ToList();
public static LibraryBook GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId) public static LibraryBook GetLibraryBook_Flat_NoTracking(this LibationContext context, string productId)
@ -91,7 +91,7 @@ namespace DataLayer
} }
#nullable disable #nullable disable
public static IEnumerable<LibraryBook> FindChildren(this IEnumerable<LibraryBook> bookList, LibraryBook parent) public static List<LibraryBook> FindChildren(this IEnumerable<LibraryBook> bookList, LibraryBook parent)
=> bookList => bookList
.Where( .Where(
lb => lb =>

View File

@ -40,30 +40,17 @@ namespace LibationUiBase.GridView
int parallelism = int.Max(1, Environment.ProcessorCount - 1); int parallelism = int.Max(1, Environment.ProcessorCount - 1);
(int numPer, int rem) = int.DivRem(products.Length, parallelism); (int batchSize, int rem) = int.DivRem(products.Length, parallelism);
if (rem != 0) numPer++; if (rem != 0) batchSize++;
var tasks = new Task<IGridEntry[]>[parallelism];
var syncContext = SynchronizationContext.Current; var syncContext = SynchronizationContext.Current;
for (int i = 0; i < parallelism; i++) //Asynchronously create an ILibraryBookEntry for every book in the library
{ var tasks = products.Chunk(batchSize).Select(batch => Task.Run(() =>
int start = i * numPer;
tasks[i] = Task.Run(() =>
{ {
SynchronizationContext.SetSynchronizationContext(syncContext); SynchronizationContext.SetSynchronizationContext(syncContext);
return batch.Select(lb => new LibraryBookEntry<TStatus>(lb) as IGridEntry);
int length = int.Min(numPer, products.Length - start); }));
if (length < 1) return Array.Empty<IGridEntry>();
var result = new IGridEntry[length];
for (int j = 0; j < length; j++)
result[j] = new LibraryBookEntry<TStatus>(products[start + j]);
return result;
});
}
return (await Task.WhenAll(tasks)).SelectMany(a => a).ToList(); return (await Task.WhenAll(tasks)).SelectMany(a => a).ToList();
} }

View File

@ -1,6 +1,5 @@
using DataLayer; using DataLayer;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
@ -62,53 +61,54 @@ namespace LibationUiBase.GridView
var seriesBooks = libraryBooks.Where(lb => lb.Book.IsEpisodeParent()).ToArray(); var seriesBooks = libraryBooks.Where(lb => lb.Book.IsEpisodeParent()).ToArray();
var allEpisodes = libraryBooks.Where(lb => lb.Book.IsEpisodeChild()).ToArray(); var allEpisodes = libraryBooks.Where(lb => lb.Book.IsEpisodeChild()).ToArray();
int parallelism = int.Max(1, Environment.ProcessorCount - 1);
var tasks = new Task[parallelism];
var syncContext = SynchronizationContext.Current;
var q = new BlockingCollection<(int, LibraryBook episode)>();
var seriesEntries = new ISeriesEntry[seriesBooks.Length]; var seriesEntries = new ISeriesEntry[seriesBooks.Length];
var seriesEpisodes = new ConcurrentBag<ILibraryBookEntry>[seriesBooks.Length]; var seriesEpisodes = new ILibraryBookEntry[seriesBooks.Length][];
for (int i = 0; i < parallelism; i++) var syncContext = SynchronizationContext.Current;
{ var options = new ParallelOptions { MaxDegreeOfParallelism = int.Max(1, Environment.ProcessorCount - 1) };
tasks[i] = Task.Run(() =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
while (q.TryTake(out var entry, -1)) //Asynchronously create an ILibraryBookEntry for every episode in the library
{ await Parallel.ForEachAsync(getAllEpisodes(), options, createEpisodeEntry);
var parent = seriesEntries[entry.Item1];
var episodeBag = seriesEpisodes[entry.Item1];
episodeBag.Add(new LibraryBookEntry<TStatus>(entry.episode, parent));
}
});
}
for (int i = 0; i <seriesBooks.Length; i++) //Match all episode entries to their corresponding parents
{ for (int i = seriesEntries.Length - 1; i >= 0; i--)
var series = seriesBooks[i];
seriesEntries[i] = new SeriesEntry<TStatus>(series, Enumerable.Empty<LibraryBook>());
seriesEpisodes[i] = new ConcurrentBag<ILibraryBookEntry>();
foreach (var ep in allEpisodes.FindChildren(series))
q.Add((i, ep));
}
q.CompleteAdding();
await Task.WhenAll(tasks);
for (int i = 0; i < seriesBooks.Length; i++)
{ {
var series = seriesEntries[i]; var series = seriesEntries[i];
series.Children.AddRange(seriesEpisodes[i].OrderByDescending(c => c.SeriesOrder));
//Sort episodes by series order descending, then add them to their parent's entry
Array.Sort(seriesEpisodes[i], (a, b) => -a.SeriesOrder.CompareTo(b.SeriesOrder));
series.Children.AddRange(seriesEpisodes[i]);
series.UpdateLibraryBook(series.LibraryBook); series.UpdateLibraryBook(series.LibraryBook);
} }
return seriesEntries.Where(s => s.Children.Count != 0).ToList(); return seriesEntries.Where(s => s.Children.Count != 0).Cast<ISeriesEntry>().ToList();
//Create a LibraryBookEntry for a single episode
ValueTask createEpisodeEntry((int seriesIndex, int episodeIndex, LibraryBook episode) data, CancellationToken cancellationToken)
{
SynchronizationContext.SetSynchronizationContext(syncContext);
var parent = seriesEntries[data.seriesIndex];
seriesEpisodes[data.seriesIndex][data.episodeIndex] = new LibraryBookEntry<TStatus>(data.episode, parent);
return ValueTask.CompletedTask;
}
//Enumeration all series episodes, along with the index to its seriesEntries entry
//and an index to its seriesEpisodes entry
IEnumerable<(int seriesIndex, int episodeIndex, LibraryBook episode)> getAllEpisodes()
{
for (int i = 0; i < seriesBooks.Length; i++)
{
var series = seriesBooks[i];
var childEpisodes = allEpisodes.FindChildren(series);
SynchronizationContext.SetSynchronizationContext(syncContext);
seriesEntries[i] = new SeriesEntry<TStatus>(series, []);
seriesEpisodes[i] = new ILibraryBookEntry[childEpisodes.Count];
for (int j = 0; j < childEpisodes.Count; j++)
yield return (i, j, childEpisodes[j]);
}
}
} }
public void RemoveChild(ILibraryBookEntry lbe) public void RemoveChild(ILibraryBookEntry lbe)