using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using API.DTOs; using API.Entities; using API.Entities.Enums; using AutoMapper; using AutoMapper.QueryableExtensions; using Microsoft.EntityFrameworkCore; namespace API.Data.Repositories; [Flags] public enum LibraryIncludes { None = 1, Series = 2, AppUser = 4, Folders = 8, // Ratings = 16 } public interface ILibraryRepository { void Add(Library library); void Update(Library library); void Delete(Library library); Task> GetLibraryDtosAsync(); Task LibraryExists(string libraryName); Task GetLibraryForIdAsync(int libraryId, LibraryIncludes includes); Task GetFullLibraryForIdAsync(int libraryId); Task GetFullLibraryForIdAsync(int libraryId, int seriesId); Task> GetLibraryDtosForUsernameAsync(string userName); Task> GetLibrariesAsync(); Task DeleteLibrary(int libraryId); Task> GetLibrariesForUserIdAsync(int userId); Task GetLibraryTypeAsync(int libraryId); Task> GetLibraryForIdsAsync(IList libraryIds); } public class LibraryRepository : ILibraryRepository { private readonly DataContext _context; private readonly IMapper _mapper; public LibraryRepository(DataContext context, IMapper mapper) { _context = context; _mapper = mapper; } public void Add(Library library) { _context.Library.Add(library); } public void Update(Library library) { _context.Entry(library).State = EntityState.Modified; } public void Delete(Library library) { _context.Library.Remove(library); } public async Task> GetLibraryDtosForUsernameAsync(string userName) { return await _context.Library .Include(l => l.AppUsers) .Where(library => library.AppUsers.Any(x => x.UserName == userName)) .OrderBy(l => l.Name) .ProjectTo(_mapper.ConfigurationProvider) .AsNoTracking() .AsSingleQuery() .ToListAsync(); } public async Task> GetLibrariesAsync() { return await _context.Library .Include(l => l.AppUsers) .ToListAsync(); } public async Task DeleteLibrary(int libraryId) { var library = await GetLibraryForIdAsync(libraryId, LibraryIncludes.Folders | LibraryIncludes.Series); _context.Library.Remove(library); return await _context.SaveChangesAsync() > 0; } public async Task> GetLibrariesForUserIdAsync(int userId) { return await _context.Library .Include(l => l.AppUsers) .Where(l => l.AppUsers.Select(ap => ap.Id).Contains(userId)) .AsNoTracking() .ToListAsync(); } public async Task GetLibraryTypeAsync(int libraryId) { return await _context.Library .Where(l => l.Id == libraryId) .AsNoTracking() .Select(l => l.Type) .SingleAsync(); } public async Task> GetLibraryForIdsAsync(IList libraryIds) { return await _context.Library .Where(x => libraryIds.Contains(x.Id)) .ToListAsync(); } public async Task> GetLibraryDtosAsync() { return await _context.Library .Include(f => f.Folders) .OrderBy(l => l.Name) .ProjectTo(_mapper.ConfigurationProvider) .AsNoTracking() .ToListAsync(); } public async Task GetLibraryForIdAsync(int libraryId, LibraryIncludes includes) { var query = _context.Library .Where(x => x.Id == libraryId); query = AddIncludesToQuery(query, includes); return await query.SingleAsync(); } private static IQueryable AddIncludesToQuery(IQueryable query, LibraryIncludes includeFlags) { if (includeFlags.HasFlag(LibraryIncludes.Folders)) { query = query.Include(l => l.Folders); } if (includeFlags.HasFlag(LibraryIncludes.Series)) { query = query.Include(l => l.Series); } if (includeFlags.HasFlag(LibraryIncludes.AppUser)) { query = query.Include(l => l.AppUsers); } return query; } /// /// This returns a Library with all it's Series -> Volumes -> Chapters. This is expensive. Should only be called when needed. /// /// /// public async Task GetFullLibraryForIdAsync(int libraryId) { return await _context.Library .Where(x => x.Id == libraryId) .Include(f => f.Folders) .Include(l => l.Series) .ThenInclude(s => s.Metadata) .Include(l => l.Series) .ThenInclude(s => s.Volumes) .ThenInclude(v => v.Chapters) .ThenInclude(c => c.Files) .AsSplitQuery() .SingleAsync(); } /// /// This is a heavy call, pulls all entities for a Library, except this version only grabs for one series id /// /// /// /// public async Task GetFullLibraryForIdAsync(int libraryId, int seriesId) { return await _context.Library .Where(x => x.Id == libraryId) .Include(f => f.Folders) .Include(l => l.Series.Where(s => s.Id == seriesId)) .ThenInclude(s => s.Metadata) .Include(l => l.Series.Where(s => s.Id == seriesId)) .ThenInclude(s => s.Volumes) .ThenInclude(v => v.Chapters) .ThenInclude(c => c.Files) .AsSplitQuery() .SingleAsync(); } public async Task LibraryExists(string libraryName) { return await _context.Library .AsNoTracking() .AnyAsync(x => x.Name == libraryName); } public async Task> GetLibrariesForUserAsync(AppUser user) { return await _context.Library .Where(library => library.AppUsers.Contains(user)) .Include(l => l.Folders) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } }