using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; namespace HyperBooru.Services; public enum FeedSortOrder { Ascending, Descending } public enum FeedSortType { Chronological, Rating } public record FeedOptions { public FeedSortType SortType { get; set; } public FeedSortOrder SortOrder { get; set; } public bool RandomPosition { get; set; } public Func, IQueryable>? IncludeProperties { get; set; } } public interface IFeedService { public IEnumerable Feed { get; } public void InitializeFeed(FeedOptions feedOptions); public Media[] Next(int count); } public class FeedService : IFeedService { private const int FeedChunkSize = 50; private FeedOptions? feedOptions; private Media? last; private IDbContextFactory dbFactory; public FeedService(IDbContextFactory dbFactory) => this.dbFactory = dbFactory; public void InitializeFeed(FeedOptions feedOptions) { this.feedOptions = feedOptions; last = null; } public IEnumerable Feed { get { last = null; while(true) { var media = Next(FeedChunkSize); if(media.Count() == 0) break; foreach(var m in media) yield return m; } } } public Media[] Next(int count) => NextDbChunk(Math.Abs(count), count < 0); private Media[] NextDbChunk(int chunkSize, bool reverse = false) { if(feedOptions is null) throw new InvalidOperationException("Feed must be initialized first"); while(true) { var db = dbFactory.CreateDbContext(); IQueryable media = db.Media; if(feedOptions.IncludeProperties is not null) media = feedOptions.IncludeProperties(media); var sortOrder = feedOptions.SortOrder; if(reverse) sortOrder = sortOrder == FeedSortOrder.Ascending ? FeedSortOrder.Descending : FeedSortOrder.Ascending; if(last is not null) { switch(feedOptions.SortType) { case FeedSortType.Chronological: if(sortOrder == FeedSortOrder.Descending) media = media.Where(m => m.ObjectId < last.ObjectId); else media = media.Where(m => m.ObjectId > last.ObjectId); break; } media = media.Where(m => m.ObjectId != last.ObjectId); } switch(feedOptions.SortType) { case FeedSortType.Chronological: if(sortOrder == FeedSortOrder.Descending) media = media.OrderByDescending(m => m.ObjectId); else media = media.OrderBy(m => m.ObjectId); break; } Media[] mediaArray = media .Take(chunkSize) .ToArray(); if(mediaArray.Count() != 0) last = mediaArray.Last(); } } }