// 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 osu.Framework.Development; using Realms; #nullable enable namespace osu.Game.Database { /// /// Provides a method of working with realm objects over longer application lifetimes. /// /// The underlying object type. public class RealmLive : ILive where T : RealmObject, IHasGuidPrimaryKey { public Guid ID { get; } public bool IsManaged => data.IsManaged; /// /// The original live data used to create this instance. /// private readonly T data; /// /// Construct a new instance of live realm data. /// /// The realm data. public RealmLive(T data) { this.data = data; ID = data.ID; } /// /// Perform a read operation on this live object. /// /// The action to perform. public void PerformRead(Action perform) { if (!IsManaged) { perform(data); return; } using (var realm = Realm.GetInstance(data.Realm.Config)) perform(realm.Find(ID)); } /// /// Perform a read operation on this live object. /// /// The action to perform. public TReturn PerformRead(Func perform) { 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); using (var realm = Realm.GetInstance(data.Realm.Config)) return perform(realm.Find(ID)); } /// /// Perform a write operation on this live object. /// /// The action to perform. public void PerformWrite(Action perform) { if (!IsManaged) throw new InvalidOperationException("Can't perform writes on a non-managed underlying value"); PerformRead(t => { var transaction = t.Realm.BeginWrite(); perform(t); transaction.Commit(); }); } public T Value { get { if (!IsManaged) return data; if (!ThreadSafety.IsUpdateThread) throw new InvalidOperationException($"Can't use {nameof(Value)} on managed objects from non-update threads"); // When using Value, we rely on garbage collection for the realm instance used to retrieve the instance. // 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. var realm = Realm.GetInstance(data.Realm.Config); return realm.Find(ID); } } public bool Equals(ILive? other) => ID == other?.ID; public override string ToString() => PerformRead(i => i.ToString()); } }