From 7cf5d63cd32bde05ee0bcbc262e5dac7595036f4 Mon Sep 17 00:00:00 2001 From: TocoToucan Date: Sun, 15 Oct 2017 00:40:41 +0300 Subject: [PATCH] Return back DatabaseBackedStore's query and populate functions --- osu.Game/Beatmaps/BeatmapInfo.cs | 9 +++- osu.Game/Beatmaps/BeatmapManager.cs | 46 ++++++++++++------- osu.Game/Beatmaps/BeatmapSetInfo.cs | 24 +++++++++- osu.Game/Beatmaps/BeatmapStore.cs | 42 ----------------- osu.Game/Database/DatabaseBackedStore.cs | 41 +++++++++++++++++ osu.Game/Database/IPopulate.cs | 7 +++ osu.Game/Rulesets/RulesetStore.cs | 19 ++------ .../Tests/Visual/TestCasePlaySongSelect.cs | 6 +-- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- osu.Game/osu.Game.csproj | 1 + 10 files changed, 117 insertions(+), 80 deletions(-) create mode 100644 osu.Game/Database/IPopulate.cs diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index c1516f17c9..621bc59786 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -6,12 +6,13 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using Newtonsoft.Json; +using osu.Game.Database; using osu.Game.IO.Serialization; using osu.Game.Rulesets; namespace osu.Game.Beatmaps { - public class BeatmapInfo : IEquatable, IJsonSerializable + public class BeatmapInfo : IEquatable, IJsonSerializable, IPopulate { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -124,5 +125,11 @@ namespace osu.Game.Beatmaps public bool BackgroundEquals(BeatmapInfo other) => other != null && BeatmapSet != null && other.BeatmapSet != null && BeatmapSet.Hash == other.BeatmapSet.Hash && (Metadata ?? BeatmapSet.Metadata).BackgroundFile == (other.Metadata ?? other.BeatmapSet.Metadata).BackgroundFile; + + public void Populate(OsuDbContext connection) + { + var entry = connection.Entry(this); + entry.Reference(nameof(Difficulty)); + } } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 955c6f4b17..d364a8fbe4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Linq.Expressions; using System.Threading.Tasks; using Ionic.Zip; using osu.Framework.Audio.Track; @@ -260,10 +259,13 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - if (!beatmaps.Delete(beatmapSet)) return; + lock (beatmaps) + { + if (!beatmaps.Delete(beatmapSet)) return; - if (!beatmapSet.Protected) - files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + if (!beatmapSet.Protected) + files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + } } /// @@ -302,6 +304,9 @@ namespace osu.Game.Beatmaps if (beatmapInfo == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo) return DefaultBeatmap; + lock (beatmaps) + beatmaps.Populate(beatmapInfo); + if (beatmapInfo.BeatmapSet == null) throw new InvalidOperationException($@"Beatmap set {beatmapInfo.BeatmapSetInfoID} is not in the local database."); @@ -333,7 +338,10 @@ namespace osu.Game.Beatmaps { lock (beatmaps) { - BeatmapSetInfo set = beatmaps.QueryBeatmapSet(query); + BeatmapSetInfo set = beatmaps.Query(query).FirstOrDefault(); + + if (set != null) + beatmaps.Populate(set); return set; } @@ -351,9 +359,9 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmapSets(Expression> query) + public List QueryBeatmapSets(Func query) { - return beatmaps.QueryBeatmapSets(query); + return beatmaps.QueryAndPopulate(query); } /// @@ -363,9 +371,15 @@ namespace osu.Game.Beatmaps /// The first result for the provided query, or null if no results were found. public BeatmapInfo QueryBeatmap(Func query) { - BeatmapInfo set = beatmaps.QueryBeatmap(query); + lock (beatmaps) + { + BeatmapInfo set = beatmaps.Query(query).FirstOrDefault(); - return set; + if (set != null) + beatmaps.Populate(set); + + return set; + } } /// @@ -373,9 +387,9 @@ namespace osu.Game.Beatmaps /// /// The query. /// Results from the provided query. - public List QueryBeatmaps(Expression> query) + public List QueryBeatmaps(Func query) { - lock (beatmaps) return beatmaps.QueryBeatmaps(query); + lock (beatmaps) return beatmaps.Query(query); } /// @@ -414,7 +428,7 @@ namespace osu.Game.Beatmaps // check if this beatmap has already been imported and exit early if so. BeatmapSetInfo beatmapSet; lock (beatmaps) - beatmapSet = beatmaps.QueryBeatmapSet(b => b.Hash == hash); + beatmapSet = beatmaps.QueryAndPopulate(b => b.Hash == hash).FirstOrDefault(); if (beatmapSet != null) { @@ -478,8 +492,8 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Metadata = null; // TODO: this should be done in a better place once we actually need to dynamically update it. - beatmap.BeatmapInfo.Ruleset = rulesets.QueryRulesetInfo(r => r.ID == beatmap.BeatmapInfo.RulesetID); - beatmap.BeatmapInfo.StarDifficulty = rulesets.QueryRulesetInfo(r => r.ID == beatmap.BeatmapInfo.RulesetID)?.CreateInstance()?.CreateDifficultyCalculator(beatmap) + beatmap.BeatmapInfo.Ruleset = rulesets.Query(r => r.ID == beatmap.BeatmapInfo.RulesetID).FirstOrDefault(); + beatmap.BeatmapInfo.StarDifficulty = rulesets.Query(r => r.ID == beatmap.BeatmapInfo.RulesetID).FirstOrDefault()?.CreateInstance()?.CreateDifficultyCalculator(beatmap) .Calculate() ?? 0; beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); @@ -493,11 +507,11 @@ namespace osu.Game.Beatmaps /// Returns a list of all usable s. /// /// A list of available . - public List GetAllUsableBeatmapSets() + public List GetAllUsableBeatmapSets(bool populate = true) { lock (beatmaps) { - return beatmaps.QueryBeatmapSets(b => !b.DeletePending); + return populate ? beatmaps.QueryAndPopulate(b => !b.DeletePending) : beatmaps.Query(b => !b.DeletePending); } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 404add2fa5..ddbc5d0dfb 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -4,10 +4,12 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using osu.Game.Database; +using osu.Game.IO; namespace osu.Game.Beatmaps { - public class BeatmapSetInfo + public class BeatmapSetInfo : IPopulate { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -34,5 +36,25 @@ namespace osu.Game.Beatmaps public List Files { get; set; } public bool Protected { get; set; } + + public void Populate(OsuDbContext connection) + { + var entry = connection.Entry(this); + entry.Collection(nameof(Beatmaps)).Load(); + entry.Reference(nameof(Metadata)).Load(); + entry.Collection(nameof(Files)).Load(); + + foreach (var beatmap in Beatmaps) + { + var beatmapEntry = connection.Entry(beatmap); + beatmapEntry.Reference(nameof(beatmap.Difficulty)).Load(); + } + + foreach (var file in Files) + { + var fileEntry = connection.Entry(file); + fileEntry.Reference(nameof(file.FileInfo)).Load(); + } + } } } diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 9c5a1a9dfc..0be502132f 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,9 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Linq; -using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using osu.Game.Database; @@ -134,45 +132,5 @@ namespace osu.Game.Beatmaps { Connection.BeatmapSetInfo.RemoveRange(Connection.BeatmapSetInfo.Where(b => b.DeletePending && !b.Protected)); } - - public BeatmapSetInfo QueryBeatmapSet(Func query) - { - return Connection.BeatmapSetInfo - .Include(b => b.Metadata) - .Include(b => b.Beatmaps).ThenInclude(b => b.Ruleset) - .Include(b => b.Beatmaps).ThenInclude(b => b.Difficulty) - .Include(b => b.Files).ThenInclude(f => f.FileInfo) - .FirstOrDefault(query); - } - - public List QueryBeatmapSets(Expression> query) - { - return Connection.BeatmapSetInfo - .Include(b => b.Metadata) - .Include(b => b.Beatmaps).ThenInclude(b => b.Ruleset) - .Include(b => b.Beatmaps).ThenInclude(b => b.Difficulty) - .Include(b => b.Files).ThenInclude(f => f.FileInfo) - .Where(query).ToList(); - } - - public BeatmapInfo QueryBeatmap(Func query) - { - return Connection.BeatmapInfo - .Include(b => b.BeatmapSet) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.Difficulty) - .FirstOrDefault(query); - } - - public List QueryBeatmaps(Expression> query) - { - return Connection.BeatmapInfo - .Include(b => b.BeatmapSet) - .Include(b => b.Metadata) - .Include(b => b.Ruleset) - .Include(b => b.Difficulty) - .Where(query).ToList(); - } } } diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index f4b6a866dc..fd891ca825 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Framework.Logging; @@ -49,6 +50,46 @@ namespace osu.Game.Database /// public void Reset() => Prepare(true); + public List Query(Func filter = null) where T : class + { + checkType(typeof(T)); + + var dbSet = Connection.GetType().GetProperties().Single(property => property.PropertyType == typeof(DbSet)).GetValue(Connection) as DbSet; + var query = dbSet.ToList(); + + if (filter != null) + query = query.Where(filter).ToList(); + + return query; + } + + /// + /// Query and populate results. + /// + /// An filter to refine results. + /// + public List QueryAndPopulate(Func filter) + where T : class, IPopulate + { + checkType(typeof(T)); + + var query = Query(filter); + foreach (var item in query) + Populate(item); + return query; + } + + /// + /// Populate a database-backed item. + /// + /// + public void Populate(IPopulate item) + { + checkType(item.GetType()); + + item.Populate(Connection); + } + private void checkType(Type type) { if (!ValidTypes.Contains(type)) diff --git a/osu.Game/Database/IPopulate.cs b/osu.Game/Database/IPopulate.cs new file mode 100644 index 0000000000..24cabf6d5a --- /dev/null +++ b/osu.Game/Database/IPopulate.cs @@ -0,0 +1,7 @@ +namespace osu.Game.Database +{ + public interface IPopulate + { + void Populate(OsuDbContext connection); + } +} diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 5a4b33df0b..8a8bbd9244 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -42,14 +42,14 @@ namespace osu.Game.Rulesets { Connection.Database.ExecuteSqlCommand("DELETE FROM RulesetInfo"); } - + var instances = loaded_assemblies.Values.Select(r => (Ruleset)Activator.CreateInstance(r, new RulesetInfo())); //add all legacy modes in correct order foreach (var r in instances.Where(r => r.LegacyID >= 0).OrderBy(r => r.LegacyID)) { var rulesetInfo = createRulesetInfo(r); - if (Connection.RulesetInfo.SingleOrDefault(rsi=>rsi.ID==rulesetInfo.ID)==null) + if (Connection.RulesetInfo.SingleOrDefault(rsi => rsi.ID == rulesetInfo.ID) == null) { Connection.RulesetInfo.Add(rulesetInfo); } @@ -108,19 +108,6 @@ namespace osu.Game.Rulesets protected override Type[] ValidTypes => new[] { typeof(RulesetInfo) }; - public RulesetInfo GetRuleset(int id) => Connection.RulesetInfo.First(r => r.ID == id); - - public RulesetInfo QueryRulesetInfo(Func query) - { - return Connection.RulesetInfo.FirstOrDefault(query); - } - - public List QueryRulesets(Func query = null) - { - var rulesets = Connection.RulesetInfo; - if (query != null) - return rulesets.Where(query).ToList(); - return rulesets.ToList(); - } + public RulesetInfo GetRuleset(int id) => Query().First(r => r.ID == id); } } diff --git a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs index 1b7f2def4d..f4c0d0b1b6 100644 --- a/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs +++ b/osu.Game/Tests/Visual/TestCasePlaySongSelect.cs @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1234 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.Query().First(), Path = "normal.osu", Version = "Normal", Difficulty = new BeatmapDifficulty @@ -86,7 +86,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1235 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.Query().First(), Path = "hard.osu", Version = "Hard", Difficulty = new BeatmapDifficulty @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual new BeatmapInfo { OnlineBeatmapID = 1236 + i, - Ruleset = rulesets.QueryRulesets().First(), + Ruleset = rulesets.Query().First(), Path = "insane.osu", Version = "Insane", Difficulty = new BeatmapDifficulty diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 811e240cee..b0953ceb7e 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Visual string instantiation = ruleset?.AssemblyQualifiedName; - foreach (var r in rulesets.QueryRulesets(rs => rs.Available && (instantiation == null || rs.InstantiationInfo == instantiation))) + foreach (var r in rulesets.Query(rs => rs.Available && (instantiation == null || rs.InstantiationInfo == instantiation))) AddStep(r.Name, () => loadPlayerFor(r)); } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8773119f26..b9154f7e77 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -273,6 +273,7 @@ + 20171014052545_Init.cs