`BeatmapSetInfo` detach support

This commit is contained in:
Dean Herbert 2022-01-07 14:17:22 +09:00
parent de076678fe
commit 8461eaab46
10 changed files with 90 additions and 10 deletions

View File

@ -35,6 +35,51 @@ namespace osu.Game.Tests.Database
[TestFixture]
public class BeatmapImporterTests : RealmTest
{
[Test]
public void TestDetach()
{
RunTestWithRealmAsync(async (realmFactory, storage) =>
{
using (var importer = new BeatmapModelManager(realmFactory, storage))
using (new RulesetStore(realmFactory, storage))
{
ILive<BeatmapSetInfo>? imported;
using (var reader = new ZipArchiveReader(TestResources.GetTestBeatmapStream()))
imported = await importer.Import(reader);
Assert.NotNull(imported);
Debug.Assert(imported != null);
BeatmapSetInfo? detached = null;
imported.PerformRead(live =>
{
var timer = new Stopwatch();
timer.Start();
detached = live.Detach();
Logger.Log($"Detach took {timer.ElapsedMilliseconds} ms");
Logger.Log($"NamedFiles: {live.Files.Count} {detached.Files.Count}");
Logger.Log($"Files: {live.Files.Select(f => f.File).Count()} {detached.Files.Select(f => f.File).Count()}");
Logger.Log($"Difficulties: {live.Beatmaps.Count} {detached.Beatmaps.Count}");
Logger.Log($"BeatmapDifficulties: {live.Beatmaps.Select(f => f.Difficulty).Count()} {detached.Beatmaps.Select(f => f.Difficulty).Count()}");
Logger.Log($"Metadata: {live.Metadata} {detached.Metadata}");
});
Logger.Log("Testing detached-ness");
Debug.Assert(detached != null);
Logger.Log($"NamedFiles: {detached.Files.Count}");
Logger.Log($"Files: {detached.Files.Select(f => f.File).Count()}");
Logger.Log($"Difficulties: {detached.Beatmaps.Count}");
Logger.Log($"BeatmapDifficulties: {detached.Beatmaps.Select(f => f.Difficulty).Count()}");
Logger.Log($"Metadata: {detached.Metadata}");
}
});
}
[Test]
public void TestImportBeatmapThenCleanup()
{

View File

@ -3,6 +3,7 @@
using System;
using System.Linq;
using AutoMapper;
using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Testing;
@ -143,6 +144,7 @@ public bool BackgroundEquals(BeatmapInfo? other) => other != null
private int rulesetID;
[Ignored]
[IgnoreMap]
public int RulesetID
{
// ReSharper disable once ConstantConditionalAccessQualifier
@ -182,6 +184,8 @@ public BeatmapDifficulty BaseDifficulty
public BeatmapInfo Clone() => this.Detach();
public override string ToString() => Metadata?.ToString() ?? base.ToString();
#endregion
}
}

View File

@ -151,7 +151,7 @@ public void Restore(BeatmapInfo beatmapInfo)
public List<BeatmapSetInfo> GetAllUsableBeatmapSets()
{
using (var context = contextFactory.CreateContext())
return context.All<BeatmapSetInfo>().Where(b => !b.DeletePending).ToList();
return context.All<BeatmapSetInfo>().Where(b => !b.DeletePending).Detach();
}
/// <summary>
@ -199,7 +199,7 @@ public IQueryable<BeatmapInfo> QueryBeatmaps(Expression<Func<BeatmapInfo, bool>>
/// </summary>
/// <param name="query">The query.</param>
/// <returns>The first result for the provided query, or null if no results were found.</returns>
public BeatmapInfo? QueryBeatmap(Expression<Func<BeatmapInfo, bool>> query) => beatmapModelManager.QueryBeatmap(query);
public BeatmapInfo? QueryBeatmap(Expression<Func<BeatmapInfo, bool>> query) => beatmapModelManager.QueryBeatmap(query)?.Detach();
/// <summary>
/// Saves an <see cref="IBeatmap"/> file against a given <see cref="BeatmapInfo"/>.

View File

@ -107,7 +107,7 @@ public virtual void Save(BeatmapInfo beatmapInfo, IBeatmap beatmapContent, ISkin
public BeatmapInfo? QueryBeatmap(Expression<Func<BeatmapInfo, bool>> query)
{
using (var context = ContextFactory.CreateContext())
return context.All<BeatmapInfo>().FirstOrDefault(query); // TODO: ?.ToLive();
return context.All<BeatmapInfo>().FirstOrDefault(query)?.Detach();
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using osu.Framework.Testing;
using osu.Game.Database;
using osu.Game.Extensions;
@ -75,7 +76,10 @@ public bool Equals(BeatmapSetInfo? other)
public bool Equals(IBeatmapSetInfo? other) => other is BeatmapSetInfo b && Equals(b);
[IgnoreMap]
IEnumerable<IBeatmapInfo> IBeatmapSetInfo.Beatmaps => Beatmaps;
[IgnoreMap]
IEnumerable<INamedFileUsage> IHasNamedFiles.Files => Files;
}
}

View File

@ -82,6 +82,10 @@ public virtual WorkingBeatmap GetWorkingBeatmap(BeatmapInfo beatmapInfo)
beatmapInfo = BeatmapManager.QueryBeatmap(b => b.ID == lookupId);
}
// TODO: FUCK THE WORLD :D
if (beatmapInfo?.IsManaged == true)
beatmapInfo = beatmapInfo.Detach();
if (beatmapInfo?.BeatmapSet == null)
return DefaultBeatmap;

View File

@ -209,7 +209,7 @@ private List<BeatmapCollection> readCollections(Stream stream, ProgressNotificat
string checksum = sr.ReadString();
var beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == checksum);
var beatmap = beatmaps.QueryBeatmap(b => b.MD5Hash == checksum)?.Detach();
if (beatmap != null)
collection.Beatmaps.Add(beatmap);
}

View File

@ -61,8 +61,9 @@ public void PerformRead(Action<T> perform)
/// <param name="perform">The action to perform.</param>
public TReturn PerformRead<TReturn>(Func<T, TReturn> perform)
{
if (typeof(RealmObjectBase).IsAssignableFrom(typeof(TReturn)))
throw new InvalidOperationException(@$"Realm live objects should not exit the scope of {nameof(PerformRead)}.");
// TODO: this is weird and kinda wrong... unmanaged objects should be allowed?
// if (typeof(RealmObjectBase).IsAssignableFrom(typeof(TReturn)))
// throw new InvalidOperationException(@$"Realm live objects should not exit the scope of {nameof(PerformRead)}.");
if (!IsManaged)
return perform(data);

View File

@ -6,7 +6,10 @@
using System.Linq;
using AutoMapper;
using osu.Framework.Development;
using osu.Game.Beatmaps;
using osu.Game.Input.Bindings;
using osu.Game.Models;
using osu.Game.Rulesets;
using Realms;
#nullable enable
@ -18,9 +21,23 @@ public static class RealmObjectExtensions
private static readonly IMapper mapper = new MapperConfiguration(c =>
{
c.ShouldMapField = fi => false;
c.ShouldMapProperty = pi => pi.SetMethod != null && pi.SetMethod.IsPublic;
c.ShouldMapProperty = pi => true;
c.CreateMap<RealmKeyBinding, RealmKeyBinding>();
c.CreateMap<BeatmapMetadata, BeatmapMetadata>();
c.CreateMap<BeatmapDifficulty, BeatmapDifficulty>();
c.CreateMap<RulesetInfo, RulesetInfo>();
c.CreateMap<RealmUser, RealmUser>();
c.CreateMap<RealmFile, RealmFile>();
c.CreateMap<RealmNamedFileUsage, RealmNamedFileUsage>();
c.CreateMap<BeatmapInfo, BeatmapInfo>();
c.CreateMap<BeatmapSetInfo, BeatmapSetInfo>();
c.ForAllMaps((a, b) =>
{
b.PreserveReferences();
b.MaxDepth(2);
});
}).CreateMapper();
/// <summary>
@ -32,7 +49,7 @@ public static class RealmObjectExtensions
/// <param name="items">A list of managed <see cref="RealmObject"/>s to detach.</param>
/// <typeparam name="T">The type of object.</typeparam>
/// <returns>A list containing non-managed copies of provided items.</returns>
public static List<T> Detach<T>(this IEnumerable<T> items) where T : RealmObject
public static List<T> Detach<T>(this IEnumerable<T> items) where T : RealmObjectBase
{
var list = new List<T>();
@ -51,7 +68,7 @@ public static List<T> Detach<T>(this IEnumerable<T> items) where T : RealmObject
/// <param name="item">The managed <see cref="RealmObject"/> to detach.</param>
/// <typeparam name="T">The type of object.</typeparam>
/// <returns>A non-managed copy of provided item. Will return the provided item if already detached.</returns>
public static T Detach<T>(this T item) where T : RealmObject
public static T Detach<T>(this T item) where T : RealmObjectBase
{
if (!item.IsManaged)
return item;
@ -65,7 +82,8 @@ public static List<ILive<T>> ToLiveUnmanaged<T>(this IEnumerable<T> realmList)
return realmList.Select(l => new RealmLiveUnmanaged<T>(l)).Cast<ILive<T>>().ToList();
}
public static ILive<T> ToLiveUnmanaged<T>(this T realmObject)
public static ILive<T> ToLiveUnmanaged<T>(this T realmObject
)
where T : RealmObject, IHasGuidPrimaryKey
{
return new RealmLiveUnmanaged<T>(realmObject);

View File

@ -662,6 +662,10 @@ private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet)
if (beatmapSet.Beatmaps.All(b => b.Hidden))
return null;
// TODO: FUCK THE WORLD :D
if (beatmapSet?.IsManaged == true)
beatmapSet = beatmapSet.Detach();
// todo: probably not required any more.
// foreach (var b in beatmapSet.Beatmaps)
// b.Metadata ??= beatmapSet.Metadata;