diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 5347953c85..022d64db03 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, IHasPrimaryKey { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 2dfc4d0fe0..c870c31a8b 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -4,10 +4,11 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; +using osu.Game.Database; namespace osu.Game.Beatmaps { - public class BeatmapSetInfo + public class BeatmapSetInfo : IHasPrimaryKey { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 2e6efee0aa..31de11cce6 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -48,10 +48,10 @@ namespace osu.Game.Beatmaps { var context = GetContext(); - if (beatmapSet.DeletePending) return false; + Refresh(ref beatmapSet, BeatmapSets); + if (beatmapSet.DeletePending) return false; beatmapSet.DeletePending = true; - context.Update(beatmapSet); context.SaveChanges(); BeatmapSetRemoved?.Invoke(beatmapSet); @@ -67,10 +67,10 @@ namespace osu.Game.Beatmaps { var context = GetContext(); - if (!beatmapSet.DeletePending) return false; + Refresh(ref beatmapSet, BeatmapSets); + if (!beatmapSet.DeletePending) return false; beatmapSet.DeletePending = false; - context.Update(beatmapSet); context.SaveChanges(); BeatmapSetAdded?.Invoke(beatmapSet); @@ -86,10 +86,10 @@ namespace osu.Game.Beatmaps { var context = GetContext(); - if (beatmap.Hidden) return false; + Refresh(ref beatmap, Beatmaps); + if (beatmap.Hidden) return false; beatmap.Hidden = true; - context.Update(beatmap); context.SaveChanges(); BeatmapHidden?.Invoke(beatmap); @@ -105,10 +105,10 @@ namespace osu.Game.Beatmaps { var context = GetContext(); - if (!beatmap.Hidden) return false; + Refresh(ref beatmap, Beatmaps); + if (!beatmap.Hidden) return false; beatmap.Hidden = false; - context.Update(beatmap); context.SaveChanges(); BeatmapRestored?.Invoke(beatmap); diff --git a/osu.Game/Database/DatabaseBackedStore.cs b/osu.Game/Database/DatabaseBackedStore.cs index 04aa8f91ae..bc1b7132eb 100644 --- a/osu.Game/Database/DatabaseBackedStore.cs +++ b/osu.Game/Database/DatabaseBackedStore.cs @@ -2,7 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; +using Microsoft.EntityFrameworkCore; using osu.Framework.Platform; namespace osu.Game.Database @@ -18,6 +21,22 @@ namespace osu.Game.Database private readonly ThreadLocal queryContext; + /// + /// Refresh an instance potentially from a different thread with a local context-tracked instance. + /// + /// The object to use as a reference when negotiating a local instance. + /// An optional lookup source which will be used to query and populate a freshly retrieved replacement. If not provided, the refreshed object will still be returned but will not have any includes. + /// A valid EF-stored type. + protected virtual void Refresh(ref T obj, IEnumerable lookupSource = null) where T : class, IHasPrimaryKey + { + var context = GetContext(); + + if (context.Entry(obj).State != EntityState.Detached) return; + + var id = obj.ID; + obj = lookupSource?.SingleOrDefault(t => t.ID == id) ?? context.Find(id); + } + /// /// Retrieve a shared context for performing lookups (or write operations on the update thread, for now). /// diff --git a/osu.Game/Database/IHasPrimaryKey.cs b/osu.Game/Database/IHasPrimaryKey.cs new file mode 100644 index 0000000000..9af9852b03 --- /dev/null +++ b/osu.Game/Database/IHasPrimaryKey.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Database +{ + public interface IHasPrimaryKey + { + int ID { get; set; } + } +} diff --git a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs index c0cecf361d..7e9e25aeff 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBinding.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBinding.cs @@ -3,11 +3,12 @@ using System.ComponentModel.DataAnnotations.Schema; using osu.Framework.Input.Bindings; +using osu.Game.Database; namespace osu.Game.Input.Bindings { [Table("KeyBinding")] - public class DatabasedKeyBinding : KeyBinding + public class DatabasedKeyBinding : KeyBinding, IHasPrimaryKey { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 53309fc72d..3407705909 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -60,7 +60,7 @@ namespace osu.Game.Input } /// - /// Retrieve s for a specified ruleset/variant content. + /// Retrieve s for a specified ruleset/variant content. /// /// The ruleset's internal ID. /// An optional variant. @@ -70,8 +70,14 @@ namespace osu.Game.Input public void Update(KeyBinding keyBinding) { + var dbKeyBinding = (DatabasedKeyBinding)keyBinding; + var context = GetContext(); - context.Update(keyBinding); + + Refresh(ref dbKeyBinding); + + dbKeyBinding.KeyCombination = keyBinding.KeyCombination; + context.SaveChanges(); KeyBindingChanged?.Invoke(); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 582eeebd48..0cfe07628a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -119,7 +119,7 @@ namespace osu.Game.Screens.Select internal void UpdateBeatmap(BeatmapInfo beatmap) { // todo: this method should not run more than once for the same BeatmapSetInfo. - var set = manager.Refresh(beatmap.BeatmapSet); + var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); // todo: this method should be smarter as to not recreate panels that haven't changed, etc. var group = groups.Find(b => b.BeatmapSet.ID == set.ID); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 26c0751f91..21141eed2d 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -282,6 +282,7 @@ + 20171019041408_InitialCreate.cs