1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:07:52 +08:00

Revert EF Core to version 2.2

This reverts commit f3faad74d5, reversing
changes made to 712e7bc7bf.

Several issues arose after migrating to 5.0, including, but possibly not
limited to, performance regressions in song select, as well as failures
when attempting to save beatmaps after metadata changes in the editor.
This commit is contained in:
Bartłomiej Dach 2021-03-21 11:01:06 +01:00
parent 0bb6fbdd38
commit a16c0641b2
18 changed files with 30 additions and 109 deletions

View File

@ -27,8 +27,8 @@
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" /> <PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
<PackageReference Include="System.IO.Packaging" Version="5.0.0" /> <PackageReference Include="System.IO.Packaging" Version="5.0.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.5" /> <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" /> <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="DiscordRichPresence" Version="1.0.175" /> <PackageReference Include="DiscordRichPresence" Version="1.0.175" />
</ItemGroup> </ItemGroup>

View File

@ -5,7 +5,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -5,7 +5,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -5,7 +5,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -5,7 +5,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -56,7 +56,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
beatmaps.Add(new BeatmapInfo beatmaps.Add(new BeatmapInfo
{ {
Ruleset = rulesets.GetRuleset(i % 4), Ruleset = rulesets.GetRuleset(i % 4),
RulesetID = i % 4, // workaround for efcore 5 compatibility.
OnlineBeatmapID = beatmapId, OnlineBeatmapID = beatmapId,
Length = length, Length = length,
BPM = bpm, BPM = bpm,

View File

@ -186,7 +186,6 @@ namespace osu.Game.Tests.Visual.SongSelect
Metadata = metadata, Metadata = metadata,
BaseDifficulty = new BeatmapDifficulty(), BaseDifficulty = new BeatmapDifficulty(),
Ruleset = ruleset, Ruleset = ruleset,
RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
StarDifficulty = difficultyIndex + 1, StarDifficulty = difficultyIndex + 1,
Version = $"SR{difficultyIndex + 1}" Version = $"SR{difficultyIndex + 1}"
}).ToList() }).ToList()

View File

@ -911,11 +911,9 @@ namespace osu.Game.Tests.Visual.SongSelect
int length = RNG.Next(30000, 200000); int length = RNG.Next(30000, 200000);
double bpm = RNG.NextSingle(80, 200); double bpm = RNG.NextSingle(80, 200);
var ruleset = getRuleset();
beatmaps.Add(new BeatmapInfo beatmaps.Add(new BeatmapInfo
{ {
Ruleset = ruleset, Ruleset = getRuleset(),
RulesetID = ruleset.ID.GetValueOrDefault(), // workaround for efcore 5 compatibility.
OnlineBeatmapID = beatmapId, OnlineBeatmapID = beatmapId,
Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})", Version = $"{beatmapId} (length {TimeSpan.FromMilliseconds(length):m\\:ss}, bpm {bpm:0.#})",
Length = length, Length = length,

View File

@ -6,7 +6,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" /> <PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Moq" Version="4.16.1" /> <PackageReference Include="Moq" Version="4.16.1" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -171,8 +171,6 @@ namespace osu.Game.Beatmaps
if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null)) if (beatmapSet.Beatmaps.Any(b => b.BaseDifficulty == null))
throw new InvalidOperationException($"Cannot import {nameof(BeatmapInfo)} with null {nameof(BeatmapInfo.BaseDifficulty)}."); 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. // check if a set already exists with the same online id, delete if it does.
if (beatmapSet.OnlineBeatmapSetID != null) if (beatmapSet.OnlineBeatmapSetID != null)
{ {

View File

@ -462,8 +462,6 @@ namespace osu.Game.Database
// Dereference the existing file info, since the file model will be removed. // Dereference the existing file info, since the file model will be removed.
if (file.FileInfo != null) if (file.FileInfo != null)
{ {
file.Requery(usage.Context);
Files.Dereference(file.FileInfo); Files.Dereference(file.FileInfo);
// This shouldn't be required, but here for safety in case the provided TModel is not being change tracked // 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)) using (Stream s = reader.GetStream(file))
{ {
var fileInfo = files.Add(s);
fileInfos.Add(new TFileModel fileInfos.Add(new TFileModel
{ {
Filename = file.Substring(prefix.Length).ToStandardisedPath(), Filename = file.Substring(prefix.Length).ToStandardisedPath(),
FileInfo = fileInfo, FileInfo = files.Add(s)
FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility.
}); });
} }
} }

View File

@ -1,71 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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
{
/// <summary>
/// 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.
/// </summary>
public static class DatabaseWorkaroundExtensions
{
/// <summary>
/// Re-query the provided model to ensure it is in a sane state. This method requires explicit implementation per model type.
/// </summary>
/// <param name="model"></param>
/// <param name="contextFactory"></param>
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<T>(List<T> 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);
}
}
}

View File

@ -3,6 +3,7 @@
using System; using System;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Statistics; using osu.Framework.Statistics;
@ -110,10 +111,10 @@ namespace osu.Game.Database
{ {
base.OnConfiguring(optionsBuilder); base.OnConfiguring(optionsBuilder);
optionsBuilder optionsBuilder
.UseSqlite(connectionString, // this is required for the time being due to the way we are querying in places like BeatmapStore.
sqliteOptions => sqliteOptions // if we ever move to having consumers file their own .Includes, or get eager loading support, this could be re-enabled.
.CommandTimeout(10) .ConfigureWarnings(warnings => warnings.Ignore(CoreEventId.IncludeIgnoredWarning))
.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)) .UseSqlite(connectionString, sqliteOptions => sqliteOptions.CommandTimeout(10))
.UseLoggerFactory(logger.Value); .UseLoggerFactory(logger.Value);
} }

View File

@ -73,7 +73,7 @@ namespace osu.Game.Scoring
} }
set set
{ {
modsJson = JsonConvert.SerializeObject(value.Select(m => new DeserializedMod { Acronym = m.Acronym })); modsJson = null;
mods = value; mods = value;
} }
} }
@ -86,7 +86,16 @@ namespace osu.Game.Scoring
[Column("Mods")] [Column("Mods")]
public string ModsJson 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 set
{ {
modsJson = value; modsJson = value;

View File

@ -53,11 +53,6 @@ namespace osu.Game.Scoring
this.configManager = configManager; this.configManager = configManager;
} }
protected override void PreImport(ScoreInfo model)
{
model.Requery(ContextFactory);
}
protected override ScoreInfo CreateModel(ArchiveReader archive) protected override ScoreInfo CreateModel(ArchiveReader archive)
{ {
if (archive == null) if (archive == null)

View File

@ -142,11 +142,6 @@ namespace osu.Game.Skinning
} }
} }
protected override void PreImport(SkinInfo model)
{
model.Requery(ContextFactory);
}
/// <summary> /// <summary>
/// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/> /// Retrieve a <see cref="Skin"/> instance for the provided <see cref="SkinInfo"/>
/// </summary> /// </summary>

View File

@ -24,10 +24,10 @@
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.2" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="5.0.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.3" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.MessagePack" Version="5.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.2" /> <PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" /> <PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2021.317.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.317.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />

View File

@ -90,6 +90,8 @@
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
<PackageReference Include="DiffPlex" Version="1.6.3" /> <PackageReference Include="DiffPlex" Version="1.6.3" />
<PackageReference Include="Humanizer" Version="2.8.26" /> <PackageReference Include="Humanizer" Version="2.8.26" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2021.317.0" /> <PackageReference Include="ppy.osu.Framework" Version="2021.317.0" />
<PackageReference Include="SharpCompress" Version="0.28.1" /> <PackageReference Include="SharpCompress" Version="0.28.1" />