Merge pull request #12003 from UselessToucan/ef_core_5

This commit is contained in:
Dean Herbert
2021-03-15 15:05:32 +09:00
committed by GitHub
18 changed files with 109 additions and 30 deletions

View File

@ -174,6 +174,8 @@ 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)
{

View File

@ -462,6 +462,8 @@ 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
@ -635,10 +637,12 @@ 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 = files.Add(s)
FileInfo = fileInfo,
FileInfoID = fileInfo.ID // workaround for efcore 5 compatibility.
});
}
}

View File

@ -0,0 +1,71 @@
// 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,7 +3,6 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using osu.Framework.Logging;
using osu.Framework.Statistics;
@ -111,10 +110,10 @@ namespace osu.Game.Database
{
base.OnConfiguring(optionsBuilder);
optionsBuilder
// 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))
.UseSqlite(connectionString,
sqliteOptions => sqliteOptions
.CommandTimeout(10)
.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))
.UseLoggerFactory(logger.Value);
}

View File

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

View File

@ -52,6 +52,11 @@ 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)

View File

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

View File

@ -24,10 +24,10 @@
<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.NewtonsoftJson" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="5.0.4" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.NETCore.Targets" Version="3.1.0" />
<PackageReference Include="Microsoft.NETCore.Targets" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2021.309.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.211.1" />