// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Game.Database; namespace osu.Game.Beatmaps { /// /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing /// public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes { public event Action BeatmapHidden; public event Action BeatmapRestored; public BeatmapStore(IDatabaseContextFactory factory) : base(factory) { } /// /// Hide a in the database. /// /// The beatmap to hide. /// Whether the beatmap's was changed. public bool Hide(BeatmapInfo beatmap) { using (ContextFactory.GetForWrite()) { Refresh(ref beatmap, Beatmaps); if (beatmap.Hidden) return false; beatmap.Hidden = true; } BeatmapHidden?.Invoke(beatmap); return true; } /// /// Restore a previously hidden . /// /// The beatmap to restore. /// Whether the beatmap's was changed. public bool Restore(BeatmapInfo beatmap) { using (ContextFactory.GetForWrite()) { Refresh(ref beatmap, Beatmaps); if (!beatmap.Hidden) return false; beatmap.Hidden = false; } BeatmapRestored?.Invoke(beatmap); return true; } protected override IQueryable AddIncludesForDeletion(IQueryable query) => base.AddIncludesForDeletion(query) .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override void Purge(List items, OsuDbContext context) { // metadata is M-N so we can't rely on cascades context.BeatmapMetadata.RemoveRange(items.Select(s => s.Metadata)); context.BeatmapMetadata.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.Metadata).Where(m => m != null))); // todo: we can probably make cascades work here with a FK in BeatmapDifficulty. just make to make it work correctly. context.BeatmapDifficulty.RemoveRange(items.SelectMany(s => s.Beatmaps.Select(b => b.BaseDifficulty))); base.Purge(items, context); } public IQueryable BeatmapSetsOverview => ContextFactory.Get().BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Beatmaps) .AsNoTracking(); public IQueryable BeatmapSetsWithoutRuleset => ContextFactory.Get().BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Files).ThenInclude(f => f.FileInfo) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) .AsNoTracking(); public IQueryable BeatmapSetsWithoutFiles => ContextFactory.Get().BeatmapSetInfo .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) .AsNoTracking(); public IQueryable Beatmaps => ContextFactory.Get().BeatmapInfo .Include(b => b.BeatmapSet).ThenInclude(s => s.Metadata) .Include(b => b.BeatmapSet).ThenInclude(s => s.Files).ThenInclude(f => f.FileInfo) .Include(b => b.Metadata) .Include(b => b.Ruleset) .Include(b => b.BaseDifficulty); } }