2021-09-30 22:46:16 +08:00
// 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 ;
2021-11-30 10:47:29 +08:00
using osu.Framework.Development ;
2021-09-30 22:46:16 +08:00
using Realms ;
#nullable enable
namespace osu.Game.Database
{
/// <summary>
/// Provides a method of working with realm objects over longer application lifetimes.
/// </summary>
/// <typeparam name="T">The underlying object type.</typeparam>
public class RealmLive < T > : ILive < T > where T : RealmObject , IHasGuidPrimaryKey
{
public Guid ID { get ; }
2021-11-29 16:21:51 +08:00
public bool IsManaged = > data . IsManaged ;
2021-11-26 13:39:35 +08:00
2021-09-30 22:46:16 +08:00
/// <summary>
/// The original live data used to create this instance.
/// </summary>
private readonly T data ;
/// <summary>
/// Construct a new instance of live realm data.
/// </summary>
/// <param name="data">The realm data.</param>
public RealmLive ( T data )
{
this . data = data ;
ID = data . ID ;
}
/// <summary>
/// Perform a read operation on this live object.
/// </summary>
/// <param name="perform">The action to perform.</param>
public void PerformRead ( Action < T > perform )
{
2021-11-30 10:47:29 +08:00
if ( ! IsManaged )
2021-09-30 22:46:16 +08:00
{
perform ( data ) ;
return ;
}
using ( var realm = Realm . GetInstance ( data . Realm . Config ) )
perform ( realm . Find < T > ( ID ) ) ;
}
/// <summary>
/// Perform a read operation on this live object.
/// </summary>
/// <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)}." ) ;
2021-11-30 10:47:29 +08:00
if ( ! IsManaged )
2021-09-30 22:46:16 +08:00
return perform ( data ) ;
using ( var realm = Realm . GetInstance ( data . Realm . Config ) )
return perform ( realm . Find < T > ( ID ) ) ;
}
/// <summary>
/// Perform a write operation on this live object.
/// </summary>
/// <param name="perform">The action to perform.</param>
2021-11-26 13:39:35 +08:00
public void PerformWrite ( Action < T > perform )
{
if ( ! IsManaged )
throw new InvalidOperationException ( "Can't perform writes on a non-managed underlying value" ) ;
2021-09-30 22:46:16 +08:00
PerformRead ( t = >
{
var transaction = t . Realm . BeginWrite ( ) ;
perform ( t ) ;
transaction . Commit ( ) ;
} ) ;
2021-11-26 13:39:35 +08:00
}
2021-09-30 22:46:16 +08:00
public T Value
{
get
{
2021-11-30 10:47:29 +08:00
if ( ! IsManaged )
2021-09-30 22:46:16 +08:00
return data ;
2021-11-30 10:47:29 +08:00
if ( ! ThreadSafety . IsUpdateThread )
throw new InvalidOperationException ( $"Can't use {nameof(Value)} on managed objects from non-update threads" ) ;
2021-09-30 22:46:16 +08:00
2021-11-30 10:47:29 +08:00
// When using Value, we rely on garbage collection for the realm instance used to retrieve the instance.
2021-11-30 10:56:53 +08:00
// As we are sure that this is on the update thread, there should always be an open and constantly refreshing realm instance to ensure file size growth is a non-issue.
2021-11-26 20:33:11 +08:00
var realm = Realm . GetInstance ( data . Realm . Config ) ;
2021-09-30 22:46:16 +08:00
2021-11-30 10:47:29 +08:00
return realm . Find < T > ( ID ) ;
2021-09-30 22:46:16 +08:00
}
}
2021-11-26 13:38:39 +08:00
public bool Equals ( ILive < T > ? other ) = > ID = = other ? . ID ;
2021-11-29 17:00:03 +08:00
public override string ToString ( ) = > PerformRead ( i = > i . ToString ( ) ) ;
2021-09-30 22:46:16 +08:00
}
}