diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index d9d23dea6b..3e0f0cb7f6 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -27,8 +27,8 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 42f70151ac..728af5124e 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -5,7 +5,7 @@
-
+
WinExe
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index e51b20c9fe..af16f39563 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -5,7 +5,7 @@
-
+
WinExe
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index f1f75148ef..3d2d1f3fec 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -5,7 +5,7 @@
-
+
WinExe
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index c9a320bdd5..fa00922706 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -5,7 +5,7 @@
-
+
WinExe
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
index 8cfe5d8af2..faa5d9e6fc 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerMatchSongSelect.cs
@@ -56,7 +56,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps.Add(new BeatmapInfo
{
Ruleset = rulesets.GetRuleset(i % 4),
- RulesetID = i % 4, // workaround for efcore 5 compatibility.
OnlineBeatmapID = beatmapId,
Length = length,
BPM = bpm,
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
index 9b8b74e6f6..53a956c77c 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapRecommendations.cs
@@ -186,7 +186,6 @@ namespace osu.Game.Tests.Visual.SongSelect
Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset,
- RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
StarDifficulty = difficultyIndex + 1,
Version = $"SR{difficultyIndex + 1}"
}).ToList()
diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
index 057b539e44..5731b1ac2c 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs
@@ -911,11 +911,9 @@ namespace osu.Game.Tests.Visual.SongSelect
int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200);
- var ruleset = getRuleset();
beatmaps.Add(new BeatmapInfo
{
- Ruleset = ruleset,
- RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
+ Ruleset = getRuleset(),
OnlineBeatmapID = beatmapId,
Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length,
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 6f8e0fac6f..e36b3cdc74 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -6,7 +6,7 @@
-
+
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 115d1b33bb..b4ea898b7d 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -171,8 +171,6 @@ namespace osu.Game.Beatmaps
if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null))
throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}.");
- beatmapSet.Requery(ContextFactory);
-
// check if a set already exists with the same online id, delete if it does.
if (beatmapSet.OnlineBeatmapSetID != null)
{
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 64428882ac..d809dbcb01 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -462,8 +462,6 @@ namespace osu.Game.Database
// Dereference the existing file info, since the file model will be removed.
if (file.FileInfo != null)
{
- file.Requery(usage.Context);
-
Files.Dereference(file.FileInfo);
// This shouldn't be required, but here for safety in case the provided TModel is not being change tracked
@@ -637,12 +635,10 @@ namespace osu.Game.Database
{
using (Stream s = reader.GetStream(file))
{
- var fileInfo = files.Add(s);
fileInfos.Add(new TFileModel
{
Filename = file.Substring(prefix.Length).ToStandardisedPath(),
- FileInfo = fileInfo,
- FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility.
+ FileInfo = files.Add(s)
});
}
}
diff --git a/osu.Game/Database/DatabaseWorkaroundExtensions.cs b/osu.Game/Database/DatabaseWorkaroundExtensions.cs
deleted file mode 100644
index a3a982f232..0000000000
--- a/osu.Game/Database/DatabaseWorkaroundExtensions.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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 osu.Game.Beatmaps;
-using osu.Game.Scoring;
-using osu.Game.Skinning;
-
-namespace osu.Game.Database
-{
- ///
- /// Extension methods which contain workarounds to make EFcore 5.x work with our existing (incorrect) thread safety.
- /// The intention is to avoid blocking package updates while we consider the future of the database backend, with a potential backend switch imminent.
- ///
- public static class DatabaseWorkaroundExtensions
- {
- ///
- /// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type.
- ///
- ///
- ///
- public static void Requery(this IHasPrimaryKey model, IDatabaseContextFactory contextFactory)
- {
- switch (model)
- {
- case SkinInfo skinInfo:
- requeryFiles(skinInfo.Files, contextFactory);
- break;
-
- case ScoreInfo scoreInfo:
- requeryFiles(scoreInfo.Beatmap.BeatmapSet.Files, contextFactory);
- requeryFiles(scoreInfo.Files, contextFactory);
- break;
-
- case BeatmapSetInfo beatmapSetInfo:
- var context = contextFactory.Get();
-
- foreach (var beatmap in beatmapSetInfo.Beatmaps)
- {
- // Workaround System.InvalidOperationException
- // The instance of entity type 'RulesetInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.
- beatmap.Ruleset = context.RulesetInfo.Find(beatmap.RulesetID);
- }
-
- requeryFiles(beatmapSetInfo.Files, contextFactory);
- break;
-
- default:
- throw new ArgumentException($"{nameof(Requery)} does not have support for the provided model type", nameof(model));
- }
-
- void requeryFiles(List files, IDatabaseContextFactory databaseContextFactory) where T : class, INamedFileInfo
- {
- var dbContext = databaseContextFactory.Get();
-
- foreach (var file in files)
- {
- Requery(file, dbContext);
- }
- }
- }
-
- public static void Requery(this INamedFileInfo file, OsuDbContext dbContext)
- {
- // Workaround System.InvalidOperationException
- // The instance of entity type 'FileInfo' cannot be tracked because another instance with the same key value for {'ID'} is already being tracked.
- file.FileInfo = dbContext.FileInfo.Find(file.FileInfoID);
- }
- }
-}
diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs
index 2342ab07d4..2aae62edea 100644
--- a/osu.Game/Database/OsuDbContext.cs
+++ b/osu.Game/Database/OsuDbContext.cs
@@ -3,6 +3,7 @@
using System;
using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
using osu.Framework.Statistics;
@@ -110,10 +111,10 @@ namespace osu.Game.Database
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
- .UseSqlite(connectionString,
- sqliteOptions => sqliteOptions
- .CommandTimeout(10)
- .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
+ // this is required for the time being due to the way we are querying in places like BeatmapStore.
+ // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled.
+ .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning))
+ .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10))
.UseLoggerFactory(logger.Value);
}
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index 78101991f6..f5192f3a40 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -73,7 +73,7 @@ namespace osu.Game.Scoring
}
set
{
- modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym }));
+ modsJson = null;
mods = value;
}
}
@@ -86,7 +86,16 @@ namespace osu.Game.Scoring
[Column("Mods")]
public string ModsJson
{
- get => modsJson;
+ get
+ {
+ if (modsJson != null)
+ return modsJson;
+
+ if (mods == null)
+ return null;
+
+ return modsJson = JsonConvert.SerializeObject(mods.Select(m => new DeserializedMod { Acronym = m.Acronym }));
+ }
set
{
modsJson = value;
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 7d0abc5996..c7ee26c248 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -53,11 +53,6 @@ namespace osu.Game.Scoring
this.configManager = configManager;
}
- protected override void PreImport(ScoreInfo model)
- {
- model.Requery(ContextFactory);
- }
-
protected override ScoreInfo CreateModel(ArchiveReader archive)
{
if (archive == null)
diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs
index 894a068b7f..9257636301 100644
--- a/osu.Game/Skinning/SkinManager.cs
+++ b/osu.Game/Skinning/SkinManager.cs
@@ -142,11 +142,6 @@ namespace osu.Game.Skinning
}
}
- protected override void PreImport(SkinInfo model)
- {
- model.Requery(ContextFactory);
- }
-
///
/// Retrieve a instance for the provided
///
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 360c522193..9731c1d5ea 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -24,10 +24,10 @@
-
-
+
+
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index b763a91dfb..11677d345e 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -90,6 +90,8 @@
+
+