From 49e020056e6024ab407967cfec43776a24258e7d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Sep 2023 14:00:39 +0200 Subject: [PATCH 01/88] Added stub --- Realm/Realm/DatabaseTypes/RealmValue.cs | 6 ++++++ Realm/Realm/DatabaseTypes/RealmValueType.cs | 15 +++++++++++++++ Realm/Realm/Extensions/CollectionExtensions.cs | 8 +++++--- .../Benchmarks.Android/Benchmarks.Android.csproj | 2 +- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 5f37c939b7..85bc00ec86 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -18,6 +18,7 @@ using System; using System.Buffers; +using System.Collections; using System.ComponentModel; using System.Diagnostics; using System.Linq; @@ -813,6 +814,11 @@ public override int GetHashCode() } } + public static RealmValue List(IList list) + { + + } + /// /// Converts a to . Equivalent to . /// diff --git a/Realm/Realm/DatabaseTypes/RealmValueType.cs b/Realm/Realm/DatabaseTypes/RealmValueType.cs index c416932927..d1069d6b48 100644 --- a/Realm/Realm/DatabaseTypes/RealmValueType.cs +++ b/Realm/Realm/DatabaseTypes/RealmValueType.cs @@ -93,5 +93,20 @@ public enum RealmValueType : byte /// The value represents a . /// Guid, + + /// + /// The value represents a . + /// + List, + + /// + /// The value represents a . + /// + Set, + + /// + /// The value represents a . + /// + Dictionary, } } diff --git a/Realm/Realm/Extensions/CollectionExtensions.cs b/Realm/Realm/Extensions/CollectionExtensions.cs index af2770d543..c68821554b 100644 --- a/Realm/Realm/Extensions/CollectionExtensions.cs +++ b/Realm/Realm/Extensions/CollectionExtensions.cs @@ -551,12 +551,14 @@ public static async Task> SubscribeAsync(this IQueryable que } [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is only used by the weaver and should not be exposed to users.")] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", + Justification = "This is only used by the weaver/source generated classes and should not be exposed to users.")] public static void PopulateCollection(ICollection source, ICollection target, bool update, bool skipDefaults) => PopulateCollectionCore(source, target, update, skipDefaults, value => value); [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is only used by the weaver and should not be exposed to users.")] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", + Justification = "This is only used by the weaver/source generated classes and should not be exposed to users.")] public static void PopulateCollection(IDictionary source, IDictionary target, bool update, bool skipDefaults) => PopulateCollectionCore(source, target, update, skipDefaults, kvp => kvp.Value); @@ -583,7 +585,7 @@ private static void PopulateCollectionCore(ICollection? source, ICollectio { Argument.NotNull(target, nameof(target)); - if (!skipDefaults || source != null) + if (!skipDefaults || source != null) //TODO Need to check what skipDefaults does { target.Clear(); } diff --git a/Tests/Benchmarks/Benchmarks.Android/Benchmarks.Android.csproj b/Tests/Benchmarks/Benchmarks.Android/Benchmarks.Android.csproj index 19567ca866..dc7c2eca95 100644 --- a/Tests/Benchmarks/Benchmarks.Android/Benchmarks.Android.csproj +++ b/Tests/Benchmarks/Benchmarks.Android/Benchmarks.Android.csproj @@ -79,7 +79,7 @@ - {BA9ED757-BBE5-486F-BE4A-8D5542504030} + {BECF4F60-9741-49A6-BD79-D59664C2682E} Benchmarks From 572704f7f561ceaeb4754ab88f5e766352fc2336 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:37:30 +0200 Subject: [PATCH 02/88] Stubs with comments --- Realm/Realm/DatabaseTypes/RealmValue.cs | 14 +++++++++----- Realm/Realm/Native/PrimitiveValue.cs | 5 +++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 85bc00ec86..f36527410c 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -62,6 +62,7 @@ namespace Realms private readonly string? _stringValue; private readonly byte[]? _dataValue; private readonly IRealmObjectBase? _objectValue; + private readonly IList? _listValue; private readonly ObjectHandle? _objectHandle; private readonly IntPtr _propertyIndex; @@ -121,6 +122,12 @@ private RealmValue(IRealmObjectBase obj) : this() _objectValue = obj; } + private RealmValue(IList list) : this() + { + Type = RealmValueType.List; + _listValue = list; + } + /// /// Gets a RealmValue representing null. /// @@ -150,6 +157,8 @@ private RealmValue(IRealmObjectBase obj) : this() [EditorBrowsable(EditorBrowsableState.Never)] public static RealmValue Object(IRealmObjectBase value) => new(value); + public static RealmValue List(IList value) => new(value); + internal static RealmValue Create(T value, RealmValueType type) { if (value is null) @@ -814,11 +823,6 @@ public override int GetHashCode() } } - public static RealmValue List(IList list) - { - - } - /// /// Converts a to . Equivalent to . /// diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index 8aabc17344..bf1f43c0a6 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -67,6 +67,11 @@ internal unsafe struct PrimitiveValue [FieldOffset(0)] private LinkValue link_value; + /** + * We need to create something similar to LinkValue (maybe ListValue) + * that will contain a IntPtr to the list, because it seems this is the only thing we need to create the list handle + */ + [FieldOffset(16)] [MarshalAs(UnmanagedType.U1)] public RealmValueType Type; From 3f0ed7f0e88c81b8ac65cd1dc40032a314005b28 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:25:27 +0200 Subject: [PATCH 03/88] Notes --- Realm/Realm/DatabaseTypes/RealmValue.cs | 8 ++++++++ Realm/Realm/Native/PrimitiveValue.cs | 12 ++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index f36527410c..82272b523d 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -122,6 +122,14 @@ private RealmValue(IRealmObjectBase obj) : this() _objectValue = obj; } + /* + * This is enough for when needing to call SetValue(RealmValue val,...) + * We get this value and we do what we need to do (maybe modify PopulateCollection..., probably yes) + * + * For the opposite, we need to find a way to save this inside primitive value, maybe + * we just need something to keep a link to the realmList + * + */ private RealmValue(IList list) : this() { Type = RealmValueType.List; diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index bf1f43c0a6..c96cf3937d 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -67,10 +67,8 @@ internal unsafe struct PrimitiveValue [FieldOffset(0)] private LinkValue link_value; - /** - * We need to create something similar to LinkValue (maybe ListValue) - * that will contain a IntPtr to the list, because it seems this is the only thing we need to create the list handle - */ + [FieldOffset(0)] + private ListValue list_value; [FieldOffset(16)] [MarshalAs(UnmanagedType.U1)] @@ -289,6 +287,12 @@ private struct LinkValue public readonly TableKey table_key; } + [StructLayout(LayoutKind.Sequential)] + private struct ListValue + { + public IntPtr list_ptr; + } + [StructLayout(LayoutKind.Sequential)] private readonly struct TimestampValue { From 197aee1f91a87857381601ffdd7cb7ffaf301a6f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:47:20 +0200 Subject: [PATCH 04/88] Various fixes --- Realm/Realm/DatabaseTypes/RealmValue.cs | 20 +++++++++-- .../Realm/Extensions/CollectionExtensions.cs | 4 ++- Realm/Realm/Handles/ObjectHandle.cs | 33 +++++++++++++++++++ Realm/Realm/Native/PrimitiveValue.cs | 19 +++++++++++ wrappers/src/marshalling.hpp | 6 ++++ wrappers/src/object_cs.cpp | 9 +++++ 6 files changed, 87 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 82272b523d..04e0c4ca3a 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -19,6 +19,7 @@ using System; using System.Buffers; using System.Collections; +using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; @@ -62,7 +63,7 @@ namespace Realms private readonly string? _stringValue; private readonly byte[]? _dataValue; private readonly IRealmObjectBase? _objectValue; - private readonly IList? _listValue; + private readonly IList? _listValue; private readonly ObjectHandle? _objectHandle; private readonly IntPtr _propertyIndex; @@ -98,6 +99,10 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? Argument.NotNull(realm, nameof(realm)); _objectValue = primitive.AsObject(realm!); break; + case RealmValueType.List: + Argument.NotNull(realm, nameof(realm)); + _listValue = primitive.AsList(realm!); + break; default: _primitiveValue = primitive; break; @@ -130,7 +135,7 @@ private RealmValue(IRealmObjectBase obj) : this() * we just need something to keep a link to the realmList * */ - private RealmValue(IList list) : this() + private RealmValue(IList list) : this() { Type = RealmValueType.List; _listValue = list; @@ -165,7 +170,7 @@ private RealmValue(IList list) : this() [EditorBrowsable(EditorBrowsableState.Never)] public static RealmValue Object(IRealmObjectBase value) => new(value); - public static RealmValue List(IList value) => new(value); + public static RealmValue List(IList value) => new(value); internal static RealmValue Create(T value, RealmValueType type) { @@ -221,6 +226,9 @@ internal static RealmValue Create(T value, RealmValueType type) } return (PrimitiveValue.Object(obj.GetObjectHandle()!), null); + case RealmValueType.List: + var realmList = _listValue as RealmList; + return (PrimitiveValue.List(realmList.Handle.Value as ListHandle), null); default: return (_primitiveValue, null); } @@ -442,6 +450,12 @@ public string AsString() return _stringValue!; } + public IList AsList() + { + EnsureType("List", RealmValueType.List); + return _listValue!; + } + /// /// Returns the stored value as a . /// diff --git a/Realm/Realm/Extensions/CollectionExtensions.cs b/Realm/Realm/Extensions/CollectionExtensions.cs index c68821554b..2f88a340d9 100644 --- a/Realm/Realm/Extensions/CollectionExtensions.cs +++ b/Realm/Realm/Extensions/CollectionExtensions.cs @@ -609,9 +609,11 @@ private static void PopulateCollectionCore(ICollection? source, ICollectio realm.Add(robj, update); } } + //TODO I think here we could need a new case for Collections of Mixed target.Add(item); } + } } -} \ No newline at end of file +} diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index b588838b43..d02acc33f1 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -45,6 +45,9 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_value", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(ObjectHandle handle, IntPtr propertyIndex, PrimitiveValue value, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_list_value", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_list_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_create_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_embedded_link(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); @@ -219,6 +222,36 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value throw new NotSupportedException($"Asymmetric objects cannot be linked to and cannot be contained in a RealmValue. Attempted to set {value} to {metadata.Schema.Name}.{propertyName}"); } } + else if (value.Type == RealmValueType.List) + { + var listPtr = NativeMethods.set_list_value(this, propertyIndex, out var listNativeException); + + var listHandle = new ListHandle(Root!, listPtr); + var realmList = new RealmList(realm, listHandle, null); + + foreach (var item in value.AsList()) + { + if (item is RealmValue { Type: RealmValueType.Object } val) + { + var wrappedObj = val.AsIRealmObject(); + if (wrappedObj is IRealmObject robj) + { + realm.Add(robj, false); //TODO Update? + } + } + //TODO I think here we could need a new case for Collections of Mixed + + realmList.Add(item); + } + + var newRealmVal = RealmValue.List(realmList); + + var (prim, handl) = newRealmVal.ToNative(); + NativeMethods.set_value(this, propertyIndex, prim, out var setValueNativeException); + handl?.Dispose(); + setValueNativeException.ThrowIfNecessary(); + return; + } var (primitive, handles) = value.ToNative(); NativeMethods.set_value(this, propertyIndex, primitive, out var nativeException); diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index c96cf3937d..07baab9dbf 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -18,6 +18,7 @@ using System; using System.Buffers; +using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; @@ -210,6 +211,18 @@ public static PrimitiveValue Object(ObjectHandle handle) }; } + public static PrimitiveValue List(ListHandle handle) + { + return new PrimitiveValue + { + Type = RealmValueType.List, + list_value = new ListValue + { + list_ptr = handle.DangerousGetHandle() + } + }; + } + public readonly bool AsBool() => int_value == 1; public readonly long AsInt() => int_value; @@ -266,6 +279,12 @@ public readonly IRealmObjectBase AsObject(Realm realm) return realm.MakeObject(objectMetadata, handle); } + public readonly RealmList AsList(Realm realm) + { + var handle = new ListHandle(realm.SharedRealmHandle, list_value.list_ptr); + return new RealmList(realm, handle, null); + } + public readonly bool TryGetObjectHandle(Realm realm, [NotNullWhen(true)] out ObjectHandle? handle) { if (Type == RealmValueType.Object) diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 4470f76331..925d8e748b 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -101,6 +101,7 @@ enum class realm_value_type : uint8_t { RLM_TYPE_OBJECT_ID, RLM_TYPE_LINK, RLM_TYPE_UUID, + RLM_TYPE_LIST }; enum class query_argument_type : uint8_t { @@ -134,6 +135,10 @@ typedef struct realm_link { TableKey table_key; } realm_link_t; +typedef struct realm_list { + List* list; +} realm_list_t; + typedef struct realm_object_id { uint8_t bytes[12]; } realm_object_id_t; @@ -156,6 +161,7 @@ typedef struct realm_value { realm_uuid_t uuid; realm_link_t link; + realm_list_t list; char data[16]; }; diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index d9259f5334..72031578ce 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -159,6 +159,15 @@ extern "C" { }); } + REALM_EXPORT void object_set_list_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) + { + handle_errors(ex, [&]() { + verify_can_set(object); + + auto prop = get_property(object, property_ndx); + }); + } + REALM_EXPORT Results* object_get_backlinks(Object& object, size_t property_ndx, NativeException::Marshallable& ex) { return handle_errors(ex, [&] { From 3f46db7cf8c931b2e7f82204a52dcc0f9d0538e7 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:47:08 +0200 Subject: [PATCH 05/88] Updated core --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 03ba58ace5..0da737b699 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 03ba58ace5d29685154a9287d1f914aabd9b4928 +Subproject commit 0da737b699bf4bcfc1a3772385cd49cd9eb9cad9 From d4601c062d9dc354e212cda85e38d51039361141 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 14 Sep 2023 14:58:33 +0200 Subject: [PATCH 06/88] Finished stub --- Realm/Realm/Handles/ObjectHandle.cs | 17 +----------- Tests/Realm.Tests/Database/RealmValueTests.cs | 26 +++++++++++++++++++ wrappers/realm-core | 2 +- wrappers/src/marshalling.hpp | 8 ++++++ wrappers/src/object_cs.cpp | 12 +++++++-- 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index d02acc33f1..d1008546b3 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -231,25 +231,10 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value foreach (var item in value.AsList()) { - if (item is RealmValue { Type: RealmValueType.Object } val) - { - var wrappedObj = val.AsIRealmObject(); - if (wrappedObj is IRealmObject robj) - { - realm.Add(robj, false); //TODO Update? - } - } - //TODO I think here we could need a new case for Collections of Mixed - + // TODO Need to add special cases for objects and other collections realmList.Add(item); } - var newRealmVal = RealmValue.List(realmList); - - var (prim, handl) = newRealmVal.ToNative(); - NativeMethods.set_value(this, propertyIndex, prim, out var setValueNativeException); - handl?.Dispose(); - setValueNativeException.ThrowIfNecessary(); return; } diff --git a/Tests/Realm.Tests/Database/RealmValueTests.cs b/Tests/Realm.Tests/Database/RealmValueTests.cs index d8fc5c1b44..5ca7846ff9 100644 --- a/Tests/Realm.Tests/Database/RealmValueTests.cs +++ b/Tests/Realm.Tests/Database/RealmValueTests.cs @@ -674,6 +674,32 @@ public void RealmValue_Reference_IsChangedCorrectly() Assert.That(savedValue == 10); } + [Test] + public void AAAARealmValueList() + { + var rvo = new RealmValueObject(); + + rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", 3 }); + + _realm.Write(() => + { + _realm.Add(rvo); + }); + + var savedValue = rvo.RealmValueProperty; + var list = savedValue.AsList(); + + Assert.That(list.Count(), Is.EqualTo(3)); + + var firstVal = list[0].AsInt16(); + var secondVal = list[1].AsString(); + var thirdVal = list[2].AsInt16(); + + Assert.That(firstVal, Is.EqualTo(1)); + Assert.That(secondVal, Is.EqualTo("two")); + Assert.That(thirdVal, Is.EqualTo(3)); + } + [Test] public void RealmValue_WhenManaged_CanChangeType() { diff --git a/wrappers/realm-core b/wrappers/realm-core index 0da737b699..cc3c496740 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 0da737b699bf4bcfc1a3772385cd49cd9eb9cad9 +Subproject commit cc3c4967407afdce1681cdd02a76552938f4ccb7 diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 925d8e748b..2be6c4e6f7 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -507,6 +507,14 @@ static inline realm_value_t to_capi(ObjLink obj_link, SharedRealm realm) return to_capi(realm->read_group().get_object(obj_link), realm); } +static inline realm_value_t to_capi(List* list) +{ + realm_value_t val{}; + val.type = realm_value_type::RLM_TYPE_LIST; + val.list.list = list; + return val; +} + static inline realm_value_t to_capi(const Mixed& value) { realm_value_t val{}; diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 72031578ce..e6bc37275b 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -106,6 +106,11 @@ extern "C" { if (!val.is_null() && val.get_type() == type_TypedLink) { *value = to_capi(val.get(), object.realm()); } + if (val.get_type() == type_List) + { + auto list = new List(object.realm(), object.get_obj(), prop.column_key); + *value = to_capi(list); + } else { *value = to_capi(std::move(val)); } @@ -159,12 +164,15 @@ extern "C" { }); } - REALM_EXPORT void object_set_list_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) + REALM_EXPORT List* object_set_list_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) { - handle_errors(ex, [&]() { + return handle_errors(ex, [&]() { verify_can_set(object); auto prop = get_property(object, property_ndx); + object.get_obj().set_collection(prop.column_key, CollectionType::List); + + return new List(object.realm(), object.get_obj(), prop.column_key); }); } From c0f01c893199a870f3295fe8b14c5b5948099a24 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:08:46 +0200 Subject: [PATCH 07/88] Removed comment [skip-ci] --- Realm/Realm/DatabaseTypes/RealmValue.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 04e0c4ca3a..fda3051c4f 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -127,14 +127,6 @@ private RealmValue(IRealmObjectBase obj) : this() _objectValue = obj; } - /* - * This is enough for when needing to call SetValue(RealmValue val,...) - * We get this value and we do what we need to do (maybe modify PopulateCollection..., probably yes) - * - * For the opposite, we need to find a way to save this inside primitive value, maybe - * we just need something to keep a link to the realmList - * - */ private RealmValue(IList list) : this() { Type = RealmValueType.List; From 877190e6ac460e9ee45f9726f9bf3d8bfa0e3a20 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 15 Sep 2023 09:49:34 +0200 Subject: [PATCH 08/88] Removed unused --- Realm/Realm/DatabaseTypes/RealmValue.cs | 3 --- Realm/Realm/Native/PrimitiveValue.cs | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index fda3051c4f..b8f971fb90 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -218,9 +218,6 @@ internal static RealmValue Create(T value, RealmValueType type) } return (PrimitiveValue.Object(obj.GetObjectHandle()!), null); - case RealmValueType.List: - var realmList = _listValue as RealmList; - return (PrimitiveValue.List(realmList.Handle.Value as ListHandle), null); default: return (_primitiveValue, null); } diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index 07baab9dbf..b5de932013 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -211,18 +211,6 @@ public static PrimitiveValue Object(ObjectHandle handle) }; } - public static PrimitiveValue List(ListHandle handle) - { - return new PrimitiveValue - { - Type = RealmValueType.List, - list_value = new ListValue - { - list_ptr = handle.DangerousGetHandle() - } - }; - } - public readonly bool AsBool() => int_value == 1; public readonly long AsInt() => int_value; From 686d5fe332e75f4186ea1753c25504db4ad90de9 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 20 Sep 2023 16:07:10 +0200 Subject: [PATCH 09/88] Added stub for lists in lists --- Realm/Realm/DatabaseTypes/RealmList.cs | 13 +++ Realm/Realm/Handles/ListHandle.cs | 12 +++ Realm/Realm/Handles/ObjectHandle.cs | 2 +- Tests/Realm.Tests/Database/RealmValueTests.cs | 26 ------ .../Database/RealmValueWithCollections.cs | 80 +++++++++++++++++++ wrappers/src/list_cs.cpp | 23 ++++++ 6 files changed, 129 insertions(+), 27 deletions(-) create mode 100644 Tests/Realm.Tests/Database/RealmValueWithCollections.cs diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index de67a582b5..2ae270eb36 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -88,6 +88,19 @@ public void Add(T value) { var realmValue = ValidateValueToInsert(value); + if (realmValue.Type == RealmValueType.List) + { + var newListHandle = _listHandle.AddList(); + var newList = new RealmList(Realm, newListHandle, null); + + foreach (var item in realmValue.AsList()) + { + newList.Add(item); + } + + return; + } + if (_isEmbedded) { if (IsDynamic) diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index bfcdcf1d54..27f3cc5045 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -32,6 +32,9 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr add_embedded(ListHandle listHandle, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_list_value", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr add_list_value(ListHandle listHandle, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_value", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(ListHandle listHandle, IntPtr targetIndex, PrimitiveValue value, out NativeException ex); @@ -140,6 +143,15 @@ public ObjectHandle AddEmbedded() return new ObjectHandle(Root!, result); } + public ListHandle AddList() + { + EnsureIsOpen(); + + var listPtr = NativeMethods.add_list_value(this, out var nativeException); + nativeException.ThrowIfNecessary(); + return new ListHandle(Root!, listPtr); + } + public void Set(int targetIndex, in RealmValue value) { EnsureIsOpen(); diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index d1008546b3..2e818f47af 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -225,13 +225,13 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value else if (value.Type == RealmValueType.List) { var listPtr = NativeMethods.set_list_value(this, propertyIndex, out var listNativeException); + //TODO Need to do something with the exception var listHandle = new ListHandle(Root!, listPtr); var realmList = new RealmList(realm, listHandle, null); foreach (var item in value.AsList()) { - // TODO Need to add special cases for objects and other collections realmList.Add(item); } diff --git a/Tests/Realm.Tests/Database/RealmValueTests.cs b/Tests/Realm.Tests/Database/RealmValueTests.cs index 5ca7846ff9..d8fc5c1b44 100644 --- a/Tests/Realm.Tests/Database/RealmValueTests.cs +++ b/Tests/Realm.Tests/Database/RealmValueTests.cs @@ -674,32 +674,6 @@ public void RealmValue_Reference_IsChangedCorrectly() Assert.That(savedValue == 10); } - [Test] - public void AAAARealmValueList() - { - var rvo = new RealmValueObject(); - - rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", 3 }); - - _realm.Write(() => - { - _realm.Add(rvo); - }); - - var savedValue = rvo.RealmValueProperty; - var list = savedValue.AsList(); - - Assert.That(list.Count(), Is.EqualTo(3)); - - var firstVal = list[0].AsInt16(); - var secondVal = list[1].AsString(); - var thirdVal = list[2].AsInt16(); - - Assert.That(firstVal, Is.EqualTo(1)); - Assert.That(secondVal, Is.EqualTo("two")); - Assert.That(thirdVal, Is.EqualTo(3)); - } - [Test] public void RealmValue_WhenManaged_CanChangeType() { diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs new file mode 100644 index 0000000000..334a0f38e3 --- /dev/null +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; + +namespace Realms.Tests.Database +{ + [TestFixture, Preserve(AllMembers = true)] + internal class RealmValueWithCollections : RealmInstanceTest + { + [Test] + public void Test1() + { + var rvo = new RealmValueObject(); + + rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", 3 }); + + _realm.Write(() => + { + _realm.Add(rvo); + }); + + var savedValue = rvo.RealmValueProperty; + var list = savedValue.AsList(); + + Assert.That(list.Count(), Is.EqualTo(3)); + + var firstVal = list[0].AsInt16(); + var secondVal = list[1].AsString(); + var thirdVal = list[2].AsInt16(); + + Assert.That(firstVal, Is.EqualTo(1)); + Assert.That(secondVal, Is.EqualTo("two")); + Assert.That(thirdVal, Is.EqualTo(3)); + } + + [Test] + public void Test2() + { + var rvo = new RealmValueObject(); + + rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", RealmValue.List(new List { 0, 15 }) }); + + _realm.Write(() => + { + _realm.Add(rvo); + }); + + var savedValue = rvo.RealmValueProperty; + var list = savedValue.AsList(); + + Assert.That(list.Count(), Is.EqualTo(3)); + + var thirdVal = list[2].AsList(); + + var firstEl = thirdVal[0].AsInt16(); + var secondEl = thirdVal[1].AsInt16(); + + Assert.That(firstEl, Is.EqualTo(0)); + Assert.That(secondEl, Is.EqualTo(15)); + } + } +} diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index 1fef7762c3..f109c05d4d 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -119,6 +119,24 @@ REALM_EXPORT void list_add_value(List& list, realm_value_t value, NativeExceptio list_insert_value(list, list.size(), value, ex); } +REALM_EXPORT List* list_insert_list_value(List& list, size_t list_ndx, NativeException::Marshallable& ex) +{ + return handle_errors(ex, [&]() { + + if (list_ndx > list.size()) { + throw IndexOutOfRangeException("Insert into RealmList", list_ndx, list.size()); + } + + list.insert_collection(list_ndx, CollectionType::List); + return new List(list.get_list(list_ndx)); + }); +} + +REALM_EXPORT List* list_add_list_value(List& list, NativeException::Marshallable& ex) +{ + return list_insert_list_value(list, list.size(), ex); +} + REALM_EXPORT Object* list_insert_embedded(List& list, size_t list_ndx, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { @@ -146,6 +164,11 @@ REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, N if (!val.is_null() && val.get_type() == type_TypedLink) { *value = to_capi(val.get(), list.get_realm()); } + else if (val.get_type() == type_List) + { + auto internalList = new List(list.get_list(ndx)); + *value = to_capi(internalList); + } else { *value = to_capi(std::move(val)); } From b9c5a8fb36c1221e5ba2112735a1ad2934d809a7 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 21 Sep 2023 11:55:17 +0200 Subject: [PATCH 10/88] Fixed error with null and added basic test with all types --- Realm/Realm/DatabaseTypes/RealmValue.cs | 3 + .../Database/RealmValueWithCollections.cs | 69 ++++++++++++++++++- wrappers/src/list_cs.cpp | 16 +++-- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index b8f971fb90..9f3570c98c 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1360,6 +1360,9 @@ public override int GetHashCode() /// A containing the supplied . public static implicit operator RealmValue(RealmObjectBase? val) => val == null ? Null : Object(val); + //TODO Add docs + public static implicit operator RealmValue(List? val) => val == null ? Null : List(val); + private void EnsureType(string target, RealmValueType type) { if (Type != type) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 334a0f38e3..25593e0f96 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -16,8 +16,10 @@ // //////////////////////////////////////////////////////////////////////////// +using System; using System.Collections.Generic; using System.Linq; +using MongoDB.Bson; using NUnit.Framework; namespace Realms.Tests.Database @@ -25,12 +27,75 @@ namespace Realms.Tests.Database [TestFixture, Preserve(AllMembers = true)] internal class RealmValueWithCollections : RealmInstanceTest { + private RealmValueObject PersistAndFind(RealmValue rv) + { + _realm.Write(() => + { + _realm.Add(new RealmValueObject { RealmValueProperty = rv }); + }); + + return _realm.All().First(); + } + + [Test] + public void TestA([Values(true, false)] bool isManaged) + { + var originalList = new List + { + RealmValue.Null, + 1, + true, + "string", + new byte[] { 0, 1, 2 }, + new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), + 1f, + 2d, + 3m, + new ObjectId("5f63e882536de46d71877979"), + Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), + new InternalObject { IntProperty = 10, StringProperty = "brown" }, + }; + + RealmValue rv = originalList; + + if (isManaged) + { + rv = PersistAndFind(rv).RealmValueProperty; + } + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.List)); + Assert.That(rv != RealmValue.Null); + + var retrievedList = rv.AsList(); + + Assert.That(retrievedList, Is.EquivalentTo(originalList)); + } + + /* To test: + * - everything works both managed and unmanaged + * - explicit/implicit conversion work + * - works with objects + * - works with lists inside lists + * - Can change type + * - Can add/replace at index + * - Can delete elements + * + * + * DONE: + * - + * + * - Doesn't cause issues with queries + * - Dynamic ? + * - sets can't contain other collections + * + */ + [Test] public void Test1() { var rvo = new RealmValueObject(); - rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", 3 }); + rvo.RealmValueProperty = new List { 1, "two", 3 }; _realm.Write(() => { @@ -56,7 +121,7 @@ public void Test2() { var rvo = new RealmValueObject(); - rvo.RealmValueProperty = RealmValue.List(new List { 1, "two", RealmValue.List(new List { 0, 15 }) }); + rvo.RealmValueProperty = new List { 1, "two", new List { 0, 15 } }; _realm.Write(() => { diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index f109c05d4d..13badad320 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -161,13 +161,17 @@ REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, N } else { auto val = list.get_any(ndx); - if (!val.is_null() && val.get_type() == type_TypedLink) { - *value = to_capi(val.get(), list.get_realm()); - } - else if (val.get_type() == type_List) + if (!val.is_null()) { - auto internalList = new List(list.get_list(ndx)); - *value = to_capi(internalList); + auto type = val.get_type(); + if (type == type_TypedLink) { + *value = to_capi(val.get(), list.get_realm()); + } + else if (type == type_List) + { + auto internalList = new List(list.get_list(ndx)); + *value = to_capi(internalList); + } } else { *value = to_capi(std::move(val)); From dfd769740672d365dc70b7b069d12e163d94ef5f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:05:46 +0200 Subject: [PATCH 11/88] Improved base testing for list, added support for add/insert/set and equality checking --- Realm/Realm/DatabaseTypes/RealmList.cs | 28 +++ Realm/Realm/DatabaseTypes/RealmValue.cs | 2 + Realm/Realm/Handles/ListHandle.cs | 30 ++- .../Database/RealmValueWithCollections.cs | 178 +++++++++++++----- wrappers/src/list_cs.cpp | 68 ++++--- 5 files changed, 228 insertions(+), 78 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 2ae270eb36..2318670b95 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -66,6 +66,20 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba ValidateIndex(index); var realmValue = ValidateValueToInsert(value); + if (realmValue.Type == RealmValueType.List) + { + var newListHandle = _listHandle.SetList(index); + var newList = new RealmList(Realm, newListHandle, null); + + foreach (var item in realmValue.AsList()) + { + newList.Add(item); + } + + return; + } + + if (_isEmbedded) { if (IsDynamic) @@ -133,6 +147,20 @@ public void Insert(int index, T value) ValidateIndex(index); var realmValue = ValidateValueToInsert(value); + //TODO Can we do something better than this, so at least we can take out the common? + if (realmValue.Type == RealmValueType.List) + { + var newListHandle = _listHandle.InsertList(index); + var newList = new RealmList(Realm, newListHandle, null); + + foreach (var item in realmValue.AsList()) + { + newList.Add(item); + } + + return; + } + if (_isEmbedded) { if (IsDynamic) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 9f3570c98c..2843d94fc4 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -759,6 +759,7 @@ public T As() RealmValueType.ObjectId => AsObjectId(), RealmValueType.Guid => AsGuid(), RealmValueType.Object => AsIRealmObject(), + RealmValueType.List => AsList(), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; } @@ -1427,6 +1428,7 @@ public bool Equals(RealmValue other) RealmValueType.ObjectId => AsObjectId() == other.AsObjectId(), RealmValueType.Guid => AsGuid() == other.AsGuid(), RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), + RealmValueType.List => AsList().SequenceEqual(other.AsList()), RealmValueType.Null => true, _ => false, }; diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index 27f3cc5045..859863564e 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -32,8 +32,8 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr add_embedded(ListHandle listHandle, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_list_value", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr add_list_value(ListHandle listHandle, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_list", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr add_list(ListHandle listHandle, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_value", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(ListHandle listHandle, IntPtr targetIndex, PrimitiveValue value, out NativeException ex); @@ -41,12 +41,18 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr set_embedded(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_list", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_list(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_value", CallingConvention = CallingConvention.Cdecl)] public static extern void insert_value(ListHandle listHandle, IntPtr targetIndex, PrimitiveValue value, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr insert_embedded(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_list", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr insert_list(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_get_value", CallingConvention = CallingConvention.Cdecl)] public static extern void get_value(ListHandle listHandle, IntPtr link_ndx, out PrimitiveValue value, out NativeException ex); @@ -147,7 +153,7 @@ public ListHandle AddList() { EnsureIsOpen(); - var listPtr = NativeMethods.add_list_value(this, out var nativeException); + var listPtr = NativeMethods.add_list(this, out var nativeException); nativeException.ThrowIfNecessary(); return new ListHandle(Root!, listPtr); } @@ -171,6 +177,15 @@ public ObjectHandle SetEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } + public ListHandle SetList(int targetIndex) + { + EnsureIsOpen(); + + var listPtr = NativeMethods.set_list(this, (IntPtr)targetIndex, out var nativeException); + nativeException.ThrowIfNecessary(); + return new ListHandle(Root!, listPtr); + } + public void Insert(int targetIndex, in RealmValue value) { EnsureIsOpen(); @@ -190,6 +205,15 @@ public ObjectHandle InsertEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } + public ListHandle InsertList(int targetIndex) + { + EnsureIsOpen(); + + var listPtr = NativeMethods.insert_list(this, (IntPtr)targetIndex, out var nativeException); + nativeException.ThrowIfNecessary(); + return new ListHandle(Root!, listPtr); + } + public int Find(in RealmValue value) { EnsureIsOpen(); diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 25593e0f96..69c32c4bfa 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -38,8 +38,11 @@ private RealmValueObject PersistAndFind(RealmValue rv) } [Test] - public void TestA([Values(true, false)] bool isManaged) + public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { + var innerList1 = new List { "inner1" }; + var innerList2 = new List { "inner2", innerList1 }; + var originalList = new List { RealmValue.Null, @@ -54,6 +57,7 @@ public void TestA([Values(true, false)] bool isManaged) new ObjectId("5f63e882536de46d71877979"), Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), new InternalObject { IntProperty = 10, StringProperty = "brown" }, + innerList2, }; RealmValue rv = originalList; @@ -66,80 +70,164 @@ public void TestA([Values(true, false)] bool isManaged) Assert.That(rv.Type, Is.EqualTo(RealmValueType.List)); Assert.That(rv != RealmValue.Null); + Assert.That(rv.AsList(), Is.EqualTo(originalList)); + Assert.That(rv == originalList); + Assert.That(rv.Equals(originalList)); + } + + [Test] + public void List_WithConstructorMethodOrOperator_WorksTheSame([Values(true, false)] bool isManaged) + { + var originalList = new List { 1, "string", true }; + + RealmValue rvOperator = originalList; + RealmValue rvConstructor = RealmValue.List(originalList); + + if (isManaged) + { + rvOperator = PersistAndFind(rvOperator).RealmValueProperty; + rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; + } + + Assert.That(rvOperator.AsList(), Is.EqualTo(originalList)); + Assert.That(rvConstructor.AsList(), Is.EqualTo(originalList)); + } + + [Test] + public void List_WhenManaged_IsNotSameReferenceAsOriginalList() + { + var originalList = new List { 1, "string", true }; + + RealmValue rv = originalList; + rv = PersistAndFind(rv).RealmValueProperty; var retrievedList = rv.AsList(); - Assert.That(retrievedList, Is.EquivalentTo(originalList)); + originalList.RemoveAt(1); + Assert.That(ReferenceEquals(originalList, retrievedList), Is.False); } - /* To test: - * - everything works both managed and unmanaged - * - explicit/implicit conversion work - * - works with objects - * - works with lists inside lists - * - Can change type - * - Can add/replace at index - * - Can delete elements - * - * - * DONE: - * - - * - * - Doesn't cause issues with queries - * - Dynamic ? - * - sets can't contain other collections - * - */ + [Test] + public void ListInsideMixed_WhenUnmanaged_IsSameReferenceAsOriginalList() + { + var originalList = new List { 1, "string", true }; + + RealmValue rv = originalList; + var retrievedList = rv.AsList(); + + originalList.RemoveAt(1); + Assert.That(ReferenceEquals(originalList, retrievedList), Is.True); + } [Test] - public void Test1() + public void List_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged) { - var rvo = new RealmValueObject(); + var stringVal = "Mario"; + var rvo = new RealmValueObject { RealmValueProperty = stringVal }; - rvo.RealmValueProperty = new List { 1, "two", 3 }; + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + Assert.That(rvo.RealmValueProperty == stringVal); + + var listVal = new List { 1, "string", true }; _realm.Write(() => { - _realm.Add(rvo); + rvo.RealmValueProperty = listVal; }); - var savedValue = rvo.RealmValueProperty; - var list = savedValue.AsList(); + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - Assert.That(list.Count(), Is.EqualTo(3)); + var newStringVal = "Luigi"; - var firstVal = list[0].AsInt16(); - var secondVal = list[1].AsString(); - var thirdVal = list[2].AsInt16(); + _realm.Write(() => + { + rvo.RealmValueProperty = newStringVal; + }); - Assert.That(firstVal, Is.EqualTo(1)); - Assert.That(secondVal, Is.EqualTo("two")); - Assert.That(thirdVal, Is.EqualTo(3)); + Assert.That(rvo.RealmValueProperty == newStringVal); } [Test] - public void Test2() + public void List_WhenManaged_CanBeModified() { - var rvo = new RealmValueObject(); + var listVal = new List { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); + }); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[1] = "Mario"; + listVal[1] = "Mario"; // To keep both list updated + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().RemoveAt(2); + listVal.RemoveAt(2); + }); - rvo.RealmValueProperty = new List { 1, "two", new List { 0, 15 } }; + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); _realm.Write(() => { - _realm.Add(rvo); + rvo.RealmValueProperty.AsList().Add("newVal"); + listVal.Add("newVal"); }); - var savedValue = rvo.RealmValueProperty; - var list = savedValue.AsList(); + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - Assert.That(list.Count(), Is.EqualTo(3)); + //TODO Maybe the set/insert/add lists and other collections can be made in another test + var innerList1 = new List { "inner", 23, false }; - var thirdVal = list[2].AsList(); + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[1] = innerList1; + listVal[1] = innerList1; + }); - var firstEl = thirdVal[0].AsInt16(); - var secondEl = thirdVal[1].AsInt16(); + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - Assert.That(firstEl, Is.EqualTo(0)); - Assert.That(secondEl, Is.EqualTo(15)); + var innerList2 = new List { "inner2", 23, false }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Insert(1, innerList2); + listVal.Insert(1, innerList2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); } + + /* To test: + * - everything works both managed and unmanaged + * - Can add/replace at index + * - Can delete elements + * + * + * DONE: + * - Works with objects + * - Works with lists inside lists + * - Explicit/implicit conversion works + * - Can changeType + * - Different equality comparer + * - Can add/replace/modify elements + * + * - Doesn't cause issues with queries + * - Dynamic ? + * - sets can't contain other collections + * - Notifications? + * + */ } } diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index 13badad320..a25b20ff54 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -46,13 +46,6 @@ namespace { extern "C" { -REALM_EXPORT Object* list_add_embedded(List& list, NativeException::Marshallable& ex) -{ - return handle_errors(ex, [&]() { - return new Object(list.get_realm(), list.get_object_schema(), list.add_embedded()); - }); -} - REALM_EXPORT void list_set_value(List& list, size_t list_ndx, realm_value_t value, NativeException::Marshallable& ex) { handle_errors(ex, [&]() { @@ -90,6 +83,19 @@ REALM_EXPORT Object* list_set_embedded(List& list, size_t list_ndx, NativeExcept }); } +REALM_EXPORT List* list_set_list(List& list, size_t list_ndx, NativeException::Marshallable& ex) +{ + return handle_errors(ex, [&]() { + + if (list_ndx > list.size()) { + throw IndexOutOfRangeException("Insert into RealmList", list_ndx, list.size()); + } + + list.set_collection(list_ndx, CollectionType::List); + return new List(list.get_list(list_ndx)); + }); +} + REALM_EXPORT void list_insert_value(List& list, size_t list_ndx, realm_value_t value, NativeException::Marshallable& ex) { handle_errors(ex, [&]() { @@ -114,12 +120,19 @@ REALM_EXPORT void list_insert_value(List& list, size_t list_ndx, realm_value_t v }); } -REALM_EXPORT void list_add_value(List& list, realm_value_t value, NativeException::Marshallable& ex) +REALM_EXPORT Object* list_insert_embedded(List& list, size_t list_ndx, NativeException::Marshallable& ex) { - list_insert_value(list, list.size(), value, ex); + return handle_errors(ex, [&]() { + const size_t count = list.size(); + if (list_ndx > count) { + throw IndexOutOfRangeException("Insert into RealmList", list_ndx, count); + } + + return new Object(list.get_realm(), list.get_object_schema(), list.insert_embedded(list_ndx)); + }); } -REALM_EXPORT List* list_insert_list_value(List& list, size_t list_ndx, NativeException::Marshallable& ex) +REALM_EXPORT List* list_insert_list(List& list, size_t list_ndx, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { @@ -132,23 +145,23 @@ REALM_EXPORT List* list_insert_list_value(List& list, size_t list_ndx, NativeExc }); } -REALM_EXPORT List* list_add_list_value(List& list, NativeException::Marshallable& ex) +REALM_EXPORT void list_add_value(List& list, realm_value_t value, NativeException::Marshallable& ex) { - return list_insert_list_value(list, list.size(), ex); + list_insert_value(list, list.size(), value, ex); } -REALM_EXPORT Object* list_insert_embedded(List& list, size_t list_ndx, NativeException::Marshallable& ex) +REALM_EXPORT Object* list_add_embedded(List& list, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { - const size_t count = list.size(); - if (list_ndx > count) { - throw IndexOutOfRangeException("Insert into RealmList", list_ndx, count); - } - - return new Object(list.get_realm(), list.get_object_schema(), list.insert_embedded(list_ndx)); + return new Object(list.get_realm(), list.get_object_schema(), list.add_embedded()); }); } +REALM_EXPORT List* list_add_list(List& list, NativeException::Marshallable& ex) +{ + return list_insert_list(list, list.size(), ex); +} + REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, NativeException::Marshallable& ex) { handle_errors(ex, [&]() { @@ -161,17 +174,12 @@ REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, N } else { auto val = list.get_any(ndx); - if (!val.is_null()) - { - auto type = val.get_type(); - if (type == type_TypedLink) { - *value = to_capi(val.get(), list.get_realm()); - } - else if (type == type_List) - { - auto internalList = new List(list.get_list(ndx)); - *value = to_capi(internalList); - } + if (!val.is_null() && val.get_type() == type_TypedLink) { + *value = to_capi(val.get(), list.get_realm()); + } + else if (!val.is_null() && val.get_type() == type_List) { + auto internalList = new List(list.get_list(ndx)); + *value = to_capi(internalList); } else { *value = to_capi(std::move(val)); From cda7a87bb13fb87dbe8144db2a8583834d863bac Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:31:51 +0200 Subject: [PATCH 12/88] Improved test and removed useless content --- Realm/Realm/DatabaseTypes/RealmList.cs | 1 - .../Realm/Extensions/CollectionExtensions.cs | 4 +- .../Database/RealmValueWithCollections.cs | 49 +++++++++++++++++-- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 2318670b95..41a61b4bc3 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -79,7 +79,6 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba return; } - if (_isEmbedded) { if (IsDynamic) diff --git a/Realm/Realm/Extensions/CollectionExtensions.cs b/Realm/Realm/Extensions/CollectionExtensions.cs index 2f88a340d9..5f78c3d5cc 100644 --- a/Realm/Realm/Extensions/CollectionExtensions.cs +++ b/Realm/Realm/Extensions/CollectionExtensions.cs @@ -585,7 +585,7 @@ private static void PopulateCollectionCore(ICollection? source, ICollectio { Argument.NotNull(target, nameof(target)); - if (!skipDefaults || source != null) //TODO Need to check what skipDefaults does + if (!skipDefaults || source != null) { target.Clear(); } @@ -609,11 +609,9 @@ private static void PopulateCollectionCore(ICollection? source, ICollectio realm.Add(robj, update); } } - //TODO I think here we could need a new case for Collections of Mixed target.Add(item); } - } } } diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 69c32c4bfa..5d640d0604 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -75,6 +75,28 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa Assert.That(rv.Equals(originalList)); } + [Test] + public void List_WhenSetBeforeBeingManaged_WorksAsIntended() + { + var originalList = new List { 1, "string", true }; + + RealmValue rv = originalList; + + var rvo = new RealmValueObject { RealmValueProperty = rv }; + + _realm.Write(() => + { + _realm.Add(rvo); + }); + + rv = rvo.RealmValueProperty; + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.List)); + Assert.That(rv != RealmValue.Null); + + Assert.That(rv.AsList(), Is.EqualTo(originalList)); + } + [Test] public void List_WithConstructorMethodOrOperator_WorksTheSame([Values(true, false)] bool isManaged) { @@ -186,8 +208,18 @@ public void List_WhenManaged_CanBeModified() }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + } + + [Test] + public void List_AddSetInsertList_WorksAsIntended() + { + var listVal = new List { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); + }); - //TODO Maybe the set/insert/add lists and other collections can be made in another test var innerList1 = new List { "inner", 23, false }; _realm.Write(() => @@ -207,15 +239,22 @@ public void List_WhenManaged_CanBeModified() }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - } + var innerList3 = new List { "inner3", 23, false }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Add(innerList3); + listVal.Add(innerList3); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + } /* To test: - * - everything works both managed and unmanaged - * - Can add/replace at index - * - Can delete elements * * * DONE: + * - everything works both managed and unmanaged * - Works with objects * - Works with lists inside lists * - Explicit/implicit conversion works From 7727f37a8d46f1c73a2b9befa27711b54310202b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:33:32 +0200 Subject: [PATCH 13/88] Added tests --- .../Database/RealmValueWithCollections.cs | 67 ++++++++++++++----- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 5d640d0604..3a2f57098d 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -250,23 +250,54 @@ public void List_AddSetInsertList_WorksAsIntended() Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); } - /* To test: - * - * - * DONE: - * - everything works both managed and unmanaged - * - Works with objects - * - Works with lists inside lists - * - Explicit/implicit conversion works - * - Can changeType - * - Different equality comparer - * - Can add/replace/modify elements - * - * - Doesn't cause issues with queries - * - Dynamic ? - * - sets can't contain other collections - * - Notifications? - * - */ + + [Test] + public void List_WhenManaged_WorksWithDynamic() + { + var originalList = new List { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.DynamicApi.Set(nameof(RealmValueObject.RealmValueProperty), originalList); + }); + + var rvp = rvo.DynamicApi.Get(nameof(RealmValueObject.RealmValueProperty)); + + Assert.That(rvp.AsList(), Is.EqualTo(originalList)); + } + + [Test] + public void List_WhenManaged_WorksWithNotifications() + { + var originalList = new List { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = originalList }); + }); + + var callbacks = new List(); + using var token = rvo.RealmValueProperty.AsList().SubscribeForNotifications((collection, changes) => + { + if (changes != null) + { + callbacks.Add(changes); + } + }); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[2] = "mario"; + }); + + _realm.Refresh(); + + Assert.That(callbacks.Count, Is.EqualTo(1)); + } } } From 2fdefc77a0198a690f19cb4186c7c78d94c2bf77 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:29:46 +0200 Subject: [PATCH 14/88] Small fixes --- Realm/Realm/DatabaseTypes/RealmList.cs | 8 ++++++-- Realm/Realm/Handles/ListHandle.cs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 41a61b4bc3..ea6699742b 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -138,6 +138,11 @@ public override int IndexOf([AllowNull] T value) return -1; } + if (realmValue.Type == RealmValueType.List) + { + return -1; + } + return _listHandle.Find(realmValue); } @@ -146,7 +151,6 @@ public void Insert(int index, T value) ValidateIndex(index); var realmValue = ValidateValueToInsert(value); - //TODO Can we do something better than this, so at least we can take out the common? if (realmValue.Type == RealmValueType.List) { var newListHandle = _listHandle.InsertList(index); @@ -191,7 +195,7 @@ public override void RemoveAt(int index) { ValidateIndex(index); - _listHandle.Erase((IntPtr)index); + _listHandle.Erase(index); } #endregion diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index 859863564e..25aa3886e6 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -225,11 +225,11 @@ public int Find(in RealmValue value) return (int)result; } - public void Erase(IntPtr rowIndex) + public void Erase(int targetIndex) { EnsureIsOpen(); - NativeMethods.erase(this, rowIndex, out var nativeException); + NativeMethods.erase(this, (IntPtr)targetIndex, out var nativeException); nativeException.ThrowIfNecessary(); } From cdeaa4cbaee69b6c7f15e19a32f2702911eb1df2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 25 Sep 2023 11:33:53 +0200 Subject: [PATCH 15/88] Small fixes --- .../Database/RealmValueWithCollections.cs | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 3a2f57098d..e4ea636ed1 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -211,7 +211,7 @@ public void List_WhenManaged_CanBeModified() } [Test] - public void List_AddSetInsertList_WorksAsIntended() + public void List_AddSetInsertMoveRemoveList_WorksAsIntended() { var listVal = new List { 1, "string", true }; @@ -222,6 +222,7 @@ public void List_AddSetInsertList_WorksAsIntended() var innerList1 = new List { "inner", 23, false }; + // Indexer _realm.Write(() => { rvo.RealmValueProperty.AsList()[1] = innerList1; @@ -230,6 +231,7 @@ public void List_AddSetInsertList_WorksAsIntended() Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + // Insert var innerList2 = new List { "inner2", 23, false }; _realm.Write(() => @@ -240,6 +242,7 @@ public void List_AddSetInsertList_WorksAsIntended() Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + // Add var innerList3 = new List { "inner3", 23, false }; _realm.Write(() => @@ -249,6 +252,40 @@ public void List_AddSetInsertList_WorksAsIntended() }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Move + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Move(0, 1); + listVal.Move(0, 1); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Remove + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().RemoveAt(2); + listVal.RemoveAt(2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + } + + public void List_RemoveWithListArgument_ReturnsFalse() + { + var innerListVal = new List { 1, "string", true }; + var listVal = new List { innerListVal, 23 }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); + }); + + _realm.Write(() => + { + Assert.That(rvo.RealmValueProperty.AsList().Remove(innerListVal), Is.False); + }); } [Test] From 0c888ce0a7c322aa60514dc406ba67962a9372f4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 27 Sep 2023 14:47:30 +0200 Subject: [PATCH 16/88] Added common method for creating a list and adding elements --- Realm/Realm/DatabaseTypes/RealmList.cs | 33 ++++++++++----------- Realm/Realm/DatabaseTypes/RealmValueType.cs | 6 ++-- Realm/Realm/Handles/ObjectHandle.cs | 9 ++---- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index ea6699742b..bb0b79288d 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -69,12 +69,7 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba if (realmValue.Type == RealmValueType.List) { var newListHandle = _listHandle.SetList(index); - var newList = new RealmList(Realm, newListHandle, null); - - foreach (var item in realmValue.AsList()) - { - newList.Add(item); - } + CreateAndAdd(Realm, newListHandle, realmValue); return; } @@ -95,6 +90,18 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba } } + internal static RealmList CreateAndAdd(Realm realm, ListHandle handle, RealmValue content) + { + var newList = new RealmList(realm, handle, null); + + foreach (var item in content.AsList()) + { + newList.Add(item); + } + + return newList; + } + #region implementing IList members public void Add(T value) @@ -104,12 +111,7 @@ public void Add(T value) if (realmValue.Type == RealmValueType.List) { var newListHandle = _listHandle.AddList(); - var newList = new RealmList(Realm, newListHandle, null); - - foreach (var item in realmValue.AsList()) - { - newList.Add(item); - } + CreateAndAdd(Realm, newListHandle, realmValue); return; } @@ -154,12 +156,7 @@ public void Insert(int index, T value) if (realmValue.Type == RealmValueType.List) { var newListHandle = _listHandle.InsertList(index); - var newList = new RealmList(Realm, newListHandle, null); - - foreach (var item in realmValue.AsList()) - { - newList.Add(item); - } + CreateAndAdd(Realm, newListHandle, realmValue); return; } diff --git a/Realm/Realm/DatabaseTypes/RealmValueType.cs b/Realm/Realm/DatabaseTypes/RealmValueType.cs index d1069d6b48..1c409d6483 100644 --- a/Realm/Realm/DatabaseTypes/RealmValueType.cs +++ b/Realm/Realm/DatabaseTypes/RealmValueType.cs @@ -95,17 +95,17 @@ public enum RealmValueType : byte Guid, /// - /// The value represents a . + /// The value represents a . /// List, /// - /// The value represents a . + /// The value represents a . /// Set, /// - /// The value represents a . + /// The value represents a . /// Dictionary, } diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index 2e818f47af..2f9b9da535 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -225,15 +225,10 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value else if (value.Type == RealmValueType.List) { var listPtr = NativeMethods.set_list_value(this, propertyIndex, out var listNativeException); - //TODO Need to do something with the exception + listNativeException.ThrowIfNecessary(); var listHandle = new ListHandle(Root!, listPtr); - var realmList = new RealmList(realm, listHandle, null); - - foreach (var item in value.AsList()) - { - realmList.Add(item); - } + RealmList.CreateAndAdd(realm, listHandle, value); return; } From 19f2167286869f4c891931bfd6e7daee142078c0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:22:33 +0200 Subject: [PATCH 17/88] Fixed error [skip-ci] --- wrappers/src/object_cs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index e6bc37275b..9b30dfe73b 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -106,7 +106,7 @@ extern "C" { if (!val.is_null() && val.get_type() == type_TypedLink) { *value = to_capi(val.get(), object.realm()); } - if (val.get_type() == type_List) + if (!val.is_null() && val.get_type() == type_List) { auto list = new List(object.realm(), object.get_obj(), prop.column_key); *value = to_capi(list); From 064a559deaabf7c8066b85eb2e7b645c8d67b765 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:40:18 +0200 Subject: [PATCH 18/88] Added set and dictionary to primitive value --- Realm/Realm/Native/PrimitiveValue.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index b5de932013..ddda090d21 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -71,6 +71,12 @@ internal unsafe struct PrimitiveValue [FieldOffset(0)] private ListValue list_value; + [FieldOffset(0)] + private SetValue set_value; + + [FieldOffset(0)] + private DictionaryValue dictionary_value; + [FieldOffset(16)] [MarshalAs(UnmanagedType.U1)] public RealmValueType Type; @@ -300,6 +306,18 @@ private struct ListValue public IntPtr list_ptr; } + [StructLayout(LayoutKind.Sequential)] + private struct SetValue + { + public IntPtr set_ptr; + } + + [StructLayout(LayoutKind.Sequential)] + private struct DictionaryValue + { + public IntPtr dict_ptr; + } + [StructLayout(LayoutKind.Sequential)] private readonly struct TimestampValue { From 9b752904cc9f9696055897450043631feb6726f8 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:30:22 +0200 Subject: [PATCH 19/88] Various improvements --- Realm/Realm/DatabaseTypes/RealmValue.cs | 105 +++++++++++++++++- Realm/Realm/Native/PrimitiveValue.cs | 12 ++ .../Database/RealmValueWithCollections.cs | 3 + 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 2843d94fc4..080fc477ce 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -63,7 +63,10 @@ namespace Realms private readonly string? _stringValue; private readonly byte[]? _dataValue; private readonly IRealmObjectBase? _objectValue; + private readonly IList? _listValue; + private readonly ISet? _setValue; + private readonly IDictionary? _dictionaryValue; private readonly ObjectHandle? _objectHandle; private readonly IntPtr _propertyIndex; @@ -103,6 +106,14 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? Argument.NotNull(realm, nameof(realm)); _listValue = primitive.AsList(realm!); break; + case RealmValueType.Set: + Argument.NotNull(realm, nameof(realm)); + _setValue = primitive.AsSet(realm!); + break; + case RealmValueType.Dictionary: + Argument.NotNull(realm, nameof(realm)); + _dictionaryValue = primitive.AsDictionary(realm!); + break; default: _primitiveValue = primitive; break; @@ -133,6 +144,18 @@ private RealmValue(IList list) : this() _listValue = list; } + private RealmValue(ISet set) : this() + { + Type = RealmValueType.Set; + _setValue = set; + } + + private RealmValue(IDictionary dict) : this() + { + Type = RealmValueType.Dictionary; + _dictionaryValue = dict; + } + /// /// Gets a RealmValue representing null. /// @@ -162,8 +185,33 @@ private RealmValue(IList list) : this() [EditorBrowsable(EditorBrowsableState.Never)] public static RealmValue Object(IRealmObjectBase value) => new(value); + /// + /// Gets a RealmValue representing a list. + /// + /// The input list to copy. + /// A new RealmValue representing the input list. + /// Once created, this RealmValue will just wrap the input collection. + /// When the object containing this RealmValue gets managed, then this value will be a Realm list. public static RealmValue List(IList value) => new(value); + /// + /// Gets a RealmValue representing a set. + /// + /// The input set to copy. + /// A new RealmValue representing the input set. + /// Once created, this RealmValue will just wrap the input collection. + /// When the object containing this RealmValue gets managed, then this value will be a Realm set. + public static RealmValue Set(ISet value) => new(value); + + /// + /// Gets a RealmValue representing a dictionary. + /// + /// The input dictionary to copy. + /// A new RealmValue representing the input dictionary. + /// Once created, this RealmValue will just wrap the input collection. + /// When the object containing this RealmValue gets managed, then this value will be a Realm dictionary. + public static RealmValue Dictionary(IDictionary value) => new(value); + internal static RealmValue Create(T value, RealmValueType type) { if (value is null) @@ -432,19 +480,46 @@ public byte[] AsData() /// Returns the stored value as a string. /// /// Thrown if the underlying value is not of type . - /// /// A string representing the value stored in the database. + /// A string representing the value stored in the database. public string AsString() { EnsureType("string", RealmValueType.String); return _stringValue!; } + /// + /// Returns the stored value as a list. + /// + /// Thrown if the underlying value is not of type . + /// A list representing the value stored in the database. public IList AsList() { EnsureType("List", RealmValueType.List); return _listValue!; } + /// + /// Returns the stored value as a set. + /// + /// Thrown if the underlying value is not of type . + /// A set representing the value stored in the database. + public ISet AsSet() + { + EnsureType("Set", RealmValueType.Set); + return _setValue!; + } + + /// + /// Returns the stored value as a dictionary. + /// + /// Thrown if the underlying value is not of type . + /// A dictionary representing the value stored in the database. + public IDictionary AsDictionary() + { + EnsureType("Dictionary", RealmValueType.Dictionary); + return _dictionaryValue!; + } + /// /// Returns the stored value as a . /// @@ -735,6 +810,7 @@ public T As() RealmValueType.ObjectId => Operator.Convert(AsObjectId()), RealmValueType.Guid => Operator.Convert(AsGuid()), RealmValueType.Object => Operator.Convert(AsIRealmObject()), + RealmValueType.List => (T)AsAny(), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; } @@ -760,6 +836,8 @@ public T As() RealmValueType.Guid => AsGuid(), RealmValueType.Object => AsIRealmObject(), RealmValueType.List => AsList(), + RealmValueType.Set => AsSet(), + RealmValueType.Dictionary => AsDictionary(), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; } @@ -827,6 +905,9 @@ public override int GetHashCode() RealmValueType.Decimal128 => AsDecimal128().GetHashCode(), RealmValueType.ObjectId => AsObjectId().GetHashCode(), RealmValueType.Object => AsIRealmObject().GetHashCode(), + RealmValueType.List => AsList().GetHashCode(), + RealmValueType.Set => AsSet().GetHashCode(), + RealmValueType.Dictionary => AsDictionary().GetHashCode(), _ => 0, }; @@ -1361,9 +1442,27 @@ public override int GetHashCode() /// A containing the supplied . public static implicit operator RealmValue(RealmObjectBase? val) => val == null ? Null : Object(val); - //TODO Add docs + /// + /// Implicitly constructs a from List<RealmValue>?. + /// + /// The value to store in the . + /// A containing the supplied . public static implicit operator RealmValue(List? val) => val == null ? Null : List(val); + /// + /// Implicitly constructs a from HashSet<RealmValue>. + /// + /// The value to store in the . + /// A containing the supplied . + public static implicit operator RealmValue(HashSet? val) => val == null ? Null : Set(val); + + /// + /// Implicitly constructs a from Dictionary<string, RealmValue>. + /// + /// The value to store in the . + /// A containing the supplied . + public static implicit operator RealmValue(Dictionary? val) => val == null ? Null : Dictionary(val); + private void EnsureType(string target, RealmValueType type) { if (Type != type) @@ -1429,6 +1528,8 @@ public bool Equals(RealmValue other) RealmValueType.Guid => AsGuid() == other.AsGuid(), RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), + RealmValueType.Set => AsSet().SequenceEqual(other.AsSet()), + RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), RealmValueType.Null => true, _ => false, }; diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index ddda090d21..439e85db07 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -279,6 +279,18 @@ public readonly RealmList AsList(Realm realm) return new RealmList(realm, handle, null); } + public readonly RealmSet AsSet(Realm realm) + { + var handle = new SetHandle(realm.SharedRealmHandle, set_value.set_ptr); + return new RealmSet(realm, handle, null); + } + + public readonly RealmDictionary AsDictionary(Realm realm) + { + var handle = new DictionaryHandle(realm.SharedRealmHandle, dictionary_value.dict_ptr); + return new RealmDictionary(realm, handle, null); + } + public readonly bool TryGetObjectHandle(Realm realm, [NotNullWhen(true)] out ObjectHandle? handle) { if (Type == RealmValueType.Object) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index e4ea636ed1..4c5b7eb666 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -71,6 +71,9 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa Assert.That(rv != RealmValue.Null); Assert.That(rv.AsList(), Is.EqualTo(originalList)); + Assert.That(rv.AsAny(), Is.EqualTo(originalList)); + Assert.That(rv.As>(), Is.EqualTo(originalList)); + Assert.That(rv == originalList); Assert.That(rv.Equals(originalList)); } From 87b7ce35d4867b06f04ee250c0c38399c69647d0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 12:56:59 +0200 Subject: [PATCH 20/88] Added conversion per list --- Realm/Realm/DatabaseTypes/RealmValue.cs | 4 +++- Realm/Realm/Helpers/Operator.cs | 14 ++++++++++++++ .../Database/RealmValueWithCollections.cs | 5 ++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 080fc477ce..fc33c80355 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -232,6 +232,8 @@ internal static RealmValue Create(T value, RealmValueType type) RealmValueType.ObjectId => ObjectId(Operator.Convert(value)), RealmValueType.Guid => Guid(Operator.Convert(value)), RealmValueType.Object => Object(Operator.Convert(value)), + RealmValueType.List => List(Operator.Convert>(value)), + //TODO Need to add list here too? _ => throw new NotSupportedException($"RealmValueType {type} is not supported."), }; } @@ -810,7 +812,7 @@ public T As() RealmValueType.ObjectId => Operator.Convert(AsObjectId()), RealmValueType.Guid => Operator.Convert(AsGuid()), RealmValueType.Object => Operator.Convert(AsIRealmObject()), - RealmValueType.List => (T)AsAny(), + RealmValueType.List => Operator.Convert, T>(AsList()), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; } diff --git a/Realm/Realm/Helpers/Operator.cs b/Realm/Realm/Helpers/Operator.cs index 2595d7a7eb..201de9ba9e 100644 --- a/Realm/Realm/Helpers/Operator.cs +++ b/Realm/Realm/Helpers/Operator.cs @@ -362,6 +362,9 @@ internal static class Operator [(typeof(Decimal128), typeof(decimal))] = new Decimal128DecimalConverter(), [(typeof(RealmValue), typeof(IRealmObjectBase))] = new RealmValueIRealmObjectBaseConverter(), [(typeof(IRealmObjectBase), typeof(RealmValue))] = new IRealmObjectBaseRealmValueConverter(), + + [(typeof(IList), typeof(RealmValue))] = new IListRealmValueConverter(), + [(typeof(RealmValue), typeof(IList))] = new RealmValueIListConverter(), }; /// @@ -801,6 +804,17 @@ private class IRealmObjectBaseRealmValueConverter : SpecializedConverterBase value is null ? RealmValue.Null : RealmValue.Object(value); } + + private class IListRealmValueConverter : SpecializedConverterBase, RealmValue> + { + public override RealmValue Convert(IList? value) => value is null ? RealmValue.Null : RealmValue.List(value); + } + + private class RealmValueIListConverter : SpecializedConverterBase> + { + public override IList Convert(RealmValue value) => value.AsList(); + } + #endregion ToRealmValue Converters #region FromRealmValue Converters diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 4c5b7eb666..a377462bf8 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -101,21 +101,24 @@ public void List_WhenSetBeforeBeingManaged_WorksAsIntended() } [Test] - public void List_WithConstructorMethodOrOperator_WorksTheSame([Values(true, false)] bool isManaged) + public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { var originalList = new List { 1, "string", true }; RealmValue rvOperator = originalList; RealmValue rvConstructor = RealmValue.List(originalList); + RealmValue rvCreate = RealmValue.Create(originalList, RealmValueType.List); if (isManaged) { rvOperator = PersistAndFind(rvOperator).RealmValueProperty; rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; + rvCreate = PersistAndFind(rvCreate).RealmValueProperty; } Assert.That(rvOperator.AsList(), Is.EqualTo(originalList)); Assert.That(rvConstructor.AsList(), Is.EqualTo(originalList)); + Assert.That(rvCreate.AsList(), Is.EqualTo(originalList)); } [Test] From 47119124a21aa7b3ba3731816b82a1fe63785334 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:11:44 +0200 Subject: [PATCH 21/88] Fixed Operator --- Realm/Realm/Helpers/Operator.cs | 30 +++++++++++++++++++--- Realm/Realm/Helpers/Operator.tt | 45 ++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/Realm/Realm/Helpers/Operator.cs b/Realm/Realm/Helpers/Operator.cs index 201de9ba9e..b0c94ddb9e 100644 --- a/Realm/Realm/Helpers/Operator.cs +++ b/Realm/Realm/Helpers/Operator.cs @@ -1,4 +1,4 @@ -//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// // // Copyright 2020 Realm Inc. // @@ -362,9 +362,12 @@ internal static class Operator [(typeof(Decimal128), typeof(decimal))] = new Decimal128DecimalConverter(), [(typeof(RealmValue), typeof(IRealmObjectBase))] = new RealmValueIRealmObjectBaseConverter(), [(typeof(IRealmObjectBase), typeof(RealmValue))] = new IRealmObjectBaseRealmValueConverter(), - [(typeof(IList), typeof(RealmValue))] = new IListRealmValueConverter(), [(typeof(RealmValue), typeof(IList))] = new RealmValueIListConverter(), + [(typeof(ISet), typeof(RealmValue))] = new ISetRealmValueConverter(), + [(typeof(RealmValue), typeof(ISet))] = new RealmValueISetConverter(), + [(typeof(IDictionary), typeof(RealmValue))] = new IDictionaryRealmValueConverter(), + [(typeof(RealmValue), typeof(IDictionary))] = new RealmValueIDictionaryConverter(), }; /// @@ -810,11 +813,15 @@ private class IListRealmValueConverter : SpecializedConverterBase? value) => value is null ? RealmValue.Null : RealmValue.List(value); } - private class RealmValueIListConverter : SpecializedConverterBase> + private class ISetRealmValueConverter : SpecializedConverterBase, RealmValue> { - public override IList Convert(RealmValue value) => value.AsList(); + public override RealmValue Convert(ISet? value) => value is null ? RealmValue.Null : RealmValue.Set(value); } + private class IDictionaryRealmValueConverter : SpecializedConverterBase, RealmValue> + { + public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); + } #endregion ToRealmValue Converters #region FromRealmValue Converters @@ -1008,6 +1015,21 @@ private class RealmValueIRealmObjectBaseConverter : SpecializedConverterBase value.AsIRealmObject(); } + + private class RealmValueIListConverter : SpecializedConverterBase> + { + public override IList Convert(RealmValue value) => value.AsList(); + } + + private class RealmValueISetConverter : SpecializedConverterBase> + { + public override ISet Convert(RealmValue value) => value.AsSet(); + } + + private class RealmValueIDictionaryConverter : SpecializedConverterBase> + { + public override IDictionary Convert(RealmValue value) => value.AsDictionary(); + } #endregion FromRealmValue Converters #region Integral Converters diff --git a/Realm/Realm/Helpers/Operator.tt b/Realm/Realm/Helpers/Operator.tt index a4c3dec819..73de520bfa 100644 --- a/Realm/Realm/Helpers/Operator.tt +++ b/Realm/Realm/Helpers/Operator.tt @@ -43,7 +43,7 @@ namespace Realms.Helpers { <# var realmValueContents = GetFileContents("../DatabaseTypes/RealmValue.cs"); - var implicitToRealmValueTypes = GetMatchedGroups(realmValueContents, _implicitTypeToRealmValueRegex, "fromType"); + var implicitToRealmValueTypes = GetMatchedGroups(realmValueContents, _implicitTypeToRealmValueRegex, "fromType").Except(_implicitTypeExcludeList); foreach (var type in implicitToRealmValueTypes) { #> @@ -75,6 +75,12 @@ namespace Realms.Helpers #> [(typeof(RealmValue), typeof(IRealmObjectBase))] = new RealmValueIRealmObjectBaseConverter(), [(typeof(IRealmObjectBase), typeof(RealmValue))] = new IRealmObjectBaseRealmValueConverter(), + [(typeof(IList), typeof(RealmValue))] = new IListRealmValueConverter(), + [(typeof(RealmValue), typeof(IList))] = new RealmValueIListConverter(), + [(typeof(ISet), typeof(RealmValue))] = new ISetRealmValueConverter(), + [(typeof(RealmValue), typeof(ISet))] = new RealmValueISetConverter(), + [(typeof(IDictionary), typeof(RealmValue))] = new IDictionaryRealmValueConverter(), + [(typeof(RealmValue), typeof(IDictionary))] = new RealmValueIDictionaryConverter(), }; /// @@ -341,6 +347,21 @@ namespace Realms.Helpers { public override RealmValue Convert(IRealmObjectBase? value) => value is null ? RealmValue.Null : RealmValue.Object(value); } + + private class IListRealmValueConverter : SpecializedConverterBase, RealmValue> + { + public override RealmValue Convert(IList? value) => value is null ? RealmValue.Null : RealmValue.List(value); + } + + private class ISetRealmValueConverter : SpecializedConverterBase, RealmValue> + { + public override RealmValue Convert(ISet? value) => value is null ? RealmValue.Null : RealmValue.Set(value); + } + + private class IDictionaryRealmValueConverter : SpecializedConverterBase, RealmValue> + { + public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); + } #endregion ToRealmValue Converters #region FromRealmValue Converters @@ -361,6 +382,21 @@ namespace Realms.Helpers { public override IRealmObjectBase Convert(RealmValue value) => value.AsIRealmObject(); } + + private class RealmValueIListConverter : SpecializedConverterBase> + { + public override IList Convert(RealmValue value) => value.AsList(); + } + + private class RealmValueISetConverter : SpecializedConverterBase> + { + public override ISet Convert(RealmValue value) => value.AsSet(); + } + + private class RealmValueIDictionaryConverter : SpecializedConverterBase> + { + public override IDictionary Convert(RealmValue value) => value.AsDictionary(); + } #endregion FromRealmValue Converters #region Integral Converters @@ -400,6 +436,13 @@ namespace Realms.Helpers private static readonly Regex _implicitTypeToRealmValueRegex = new Regex(@"public static implicit operator RealmValue\((?\S*)"); private static readonly Regex _explicitRealmValueToTypeRegex = new Regex(@"public static explicit operator (?[^\(]*)\(RealmValue"); + private static readonly List _implicitTypeExcludeList = new() + { + "List?", + "HashSet?", + "Dictionary Date: Thu, 28 Sep 2023 13:14:07 +0200 Subject: [PATCH 22/88] Added missing conversion methods --- Realm/Realm/DatabaseTypes/RealmValue.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index fc33c80355..928714b897 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -233,7 +233,8 @@ internal static RealmValue Create(T value, RealmValueType type) RealmValueType.Guid => Guid(Operator.Convert(value)), RealmValueType.Object => Object(Operator.Convert(value)), RealmValueType.List => List(Operator.Convert>(value)), - //TODO Need to add list here too? + RealmValueType.Set => Set(Operator.Convert>(value)), + RealmValueType.Dictionary => Dictionary(Operator.Convert>(value)), _ => throw new NotSupportedException($"RealmValueType {type} is not supported."), }; } @@ -813,6 +814,8 @@ public T As() RealmValueType.Guid => Operator.Convert(AsGuid()), RealmValueType.Object => Operator.Convert(AsIRealmObject()), RealmValueType.List => Operator.Convert, T>(AsList()), + RealmValueType.Set => Operator.Convert, T>(AsSet()), + RealmValueType.Dictionary => Operator.Convert, T>(AsDictionary()), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; } From acf2b825924dc353d26ceb274065061286fc8282 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 13:26:48 +0200 Subject: [PATCH 23/88] Corrected wrapper --- wrappers/src/object_cs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 9b30dfe73b..d805c78fd9 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -106,7 +106,7 @@ extern "C" { if (!val.is_null() && val.get_type() == type_TypedLink) { *value = to_capi(val.get(), object.realm()); } - if (!val.is_null() && val.get_type() == type_List) + else if (!val.is_null() && val.get_type() == type_List) { auto list = new List(object.realm(), object.get_obj(), prop.column_key); *value = to_capi(list); From bbc8f43d29b83d794370873f96b81d5483304d14 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:23:38 +0200 Subject: [PATCH 24/88] Added test helper method for realm value types and removed unused reflextion extension method --- Realm/Realm/DatabaseTypes/RealmValueType.cs | 6 +++--- Realm/Realm/Extensions/ReflectionExtensions.cs | 2 -- Tests/Realm.Tests/Database/ObjectSchemaTests.cs | 6 ++---- Tests/Realm.Tests/Database/RealmValueTests.cs | 2 +- Tests/Realm.Tests/TestHelpers.cs | 6 ++++++ wrappers/src/object_cs.cpp | 3 +-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValueType.cs b/Realm/Realm/DatabaseTypes/RealmValueType.cs index 1c409d6483..ab60b1776c 100644 --- a/Realm/Realm/DatabaseTypes/RealmValueType.cs +++ b/Realm/Realm/DatabaseTypes/RealmValueType.cs @@ -95,17 +95,17 @@ public enum RealmValueType : byte Guid, /// - /// The value represents a . + /// The value represents a . /// List, /// - /// The value represents a . + /// The value represents a . /// Set, /// - /// The value represents a . + /// The value represents a . /// Dictionary, } diff --git a/Realm/Realm/Extensions/ReflectionExtensions.cs b/Realm/Realm/Extensions/ReflectionExtensions.cs index fafb8da79a..d0b0cce93d 100644 --- a/Realm/Realm/Extensions/ReflectionExtensions.cs +++ b/Realm/Realm/Extensions/ReflectionExtensions.cs @@ -67,7 +67,5 @@ public static ObjectSchema.ObjectType GetRealmSchemaType(this Type type) return ObjectSchema.ObjectType.RealmObject; } - - public static T[] GetEnumValues() => Enum.GetValues(typeof(T)).Cast().ToArray(); } } diff --git a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs index 53db7773e7..d461fc5650 100644 --- a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs +++ b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs @@ -313,11 +313,9 @@ public void Property_FromTypeGeneric_Object( } } - public static readonly RealmValueType[] PrimitiveTypes = ReflectionExtensions.GetEnumValues(); - [Test] public void Property_Primitive_Tests( - [ValueSource(nameof(PrimitiveTypes))] RealmValueType type, + [ValueSource(nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, [ValueSource(nameof(BoolValues))] bool isPrimaryKey, [ValueSource(nameof(IndexTypes))] IndexType indexType, [ValueSource(nameof(BoolValues))] bool isNullable) @@ -369,7 +367,7 @@ public void Property_Primitive_Tests( [Test] public void Property_PrimitiveCollection_Tests( [ValueSource(nameof(CollectionTypes))] PropertyType collectionType, - [ValueSource(nameof(PrimitiveTypes))] RealmValueType type, + [ValueSource(nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, [ValueSource(nameof(BoolValues))] bool isNullable) { Property getProperty() => collectionType switch diff --git a/Tests/Realm.Tests/Database/RealmValueTests.cs b/Tests/Realm.Tests/Database/RealmValueTests.cs index d8fc5c1b44..8427e5f188 100644 --- a/Tests/Realm.Tests/Database/RealmValueTests.cs +++ b/Tests/Realm.Tests/Database/RealmValueTests.cs @@ -1010,7 +1010,7 @@ public void Query_Generic() Assert.That(f, Is.EquivalentTo(referenceResult)); } - foreach (var type in (RealmValueType[])Enum.GetValues(typeof(RealmValueType))) + foreach (var type in TestHelpers.PrimitiveRealmValueTypes) { // Equality on RealmValueType var referenceResult = rvObjects.Where(r => r.RealmValueProperty.Type == type).OrderBy(r => r.Id).ToList(); diff --git a/Tests/Realm.Tests/TestHelpers.cs b/Tests/Realm.Tests/TestHelpers.cs index 820e08842d..b06b2374cd 100644 --- a/Tests/Realm.Tests/TestHelpers.cs +++ b/Tests/Realm.Tests/TestHelpers.cs @@ -410,5 +410,11 @@ public class StrongBox public static implicit operator StrongBox(T value) => new() { Value = value }; } + + public static IEnumerable CollectionRealmValueTypes = + new[] { RealmValueType.List, RealmValueType.Set, RealmValueType.Dictionary }; + + public static IEnumerable PrimitiveRealmValueTypes = ((RealmValueType[])Enum.GetValues(typeof(RealmValueType))) + .Except(CollectionRealmValueTypes); } } diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index d805c78fd9..3569488234 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -108,8 +108,7 @@ extern "C" { } else if (!val.is_null() && val.get_type() == type_List) { - auto list = new List(object.realm(), object.get_obj(), prop.column_key); - *value = to_capi(list); + *value = to_capi(new List(object.realm(), object.get_obj(), prop.column_key)); } else { *value = to_capi(std::move(val)); From 4cb30202c699f191533248bdf63ba4eac209bf4c Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:46:14 +0200 Subject: [PATCH 25/88] Added missing types for marshaling --- wrappers/src/marshalling.hpp | 38 +++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 2be6c4e6f7..8c9484fbbe 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -101,7 +101,9 @@ enum class realm_value_type : uint8_t { RLM_TYPE_OBJECT_ID, RLM_TYPE_LINK, RLM_TYPE_UUID, - RLM_TYPE_LIST + RLM_TYPE_LIST, + RLM_TYPE_SET, + RLM_TYPE_DICTIONARY, }; enum class query_argument_type : uint8_t { @@ -136,9 +138,17 @@ typedef struct realm_link { } realm_link_t; typedef struct realm_list { - List* list; + List* list_ptr; } realm_list_t; +typedef struct realm_set { + object_store::Set* set_ptr; +} realm_set_t; + +typedef struct realm_dict { + Dictionary* dictionary_ptr; +} realm_dict_t; + typedef struct realm_object_id { uint8_t bytes[12]; } realm_object_id_t; @@ -162,6 +172,8 @@ typedef struct realm_value { realm_link_t link; realm_list_t list; + realm_set_t set; + realm_dict_t dictionary; char data[16]; }; @@ -511,7 +523,23 @@ static inline realm_value_t to_capi(List* list) { realm_value_t val{}; val.type = realm_value_type::RLM_TYPE_LIST; - val.list.list = list; + val.list.list_ptr = list; + return val; +} + +static inline realm_value_t to_capi(object_store::Set* set) +{ + realm_value_t val{}; + val.type = realm_value_type::RLM_TYPE_SET; + val.set.set_ptr = set; + return val; +} + +static inline realm_value_t to_capi(Dictionary* dictionary) +{ + realm_value_t val{}; + val.type = realm_value_type::RLM_TYPE_DICTIONARY; + val.dictionary.dictionary_ptr = dictionary; return val; } @@ -577,6 +605,10 @@ static inline realm_value_t to_capi(const Mixed& value) val.uuid = to_capi(value.get()); break; } + case type_List: + case type_Set: + case type_Dictionary: + REALM_TERMINATE("Can't use this overload of to_capi on values containing collections, use to_capi(Collection*) instead."); default: REALM_TERMINATE("Invalid Mixed value type"); } From b0e4cd967e52df87468e152e87d5498173d2ca0e Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:48:55 +0200 Subject: [PATCH 26/88] Small improvement in operator --- Realm/Realm/Helpers/Operator.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Realm/Realm/Helpers/Operator.tt b/Realm/Realm/Helpers/Operator.tt index 73de520bfa..f0c87aee01 100644 --- a/Realm/Realm/Helpers/Operator.tt +++ b/Realm/Realm/Helpers/Operator.tt @@ -436,7 +436,7 @@ namespace Realms.Helpers private static readonly Regex _implicitTypeToRealmValueRegex = new Regex(@"public static implicit operator RealmValue\((?\S*)"); private static readonly Regex _explicitRealmValueToTypeRegex = new Regex(@"public static explicit operator (?[^\(]*)\(RealmValue"); - private static readonly List _implicitTypeExcludeList = new() + private static readonly string[] _implicitTypeExcludeList = new[] { "List?", "HashSet?", From 4785943f0a66ec4d1bc26f778b87af8cf42f1ee6 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:21:08 +0200 Subject: [PATCH 27/88] Small corrections --- Realm/Realm/DatabaseTypes/RealmDictionary.cs | 12 ++++++++ Realm/Realm/DatabaseTypes/RealmList.cs | 24 +++++++-------- Realm/Realm/DatabaseTypes/RealmSet.cs | 12 ++++++++ Realm/Realm/DatabaseTypes/RealmValue.cs | 4 +-- Realm/Realm/Handles/ObjectHandle.cs | 30 +++++++++++++++++-- .../Database/RealmValueWithCollections.cs | 2 ++ wrappers/src/marshalling.hpp | 4 +-- wrappers/src/object_cs.cpp | 24 +++++++++++++++ 8 files changed, 94 insertions(+), 18 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index 230c7a91bc..1e8ba2cc8a 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -243,6 +243,18 @@ private static void ValidateKey(string key) } } + internal static RealmDictionary CreateAndAdd(Realm realm, DictionaryHandle handle, RealmValue content) + { + var newDictionary = new RealmDictionary(realm, handle, null); + + foreach (var item in content.AsList()) + { + newDictionary.Add(item); + } + + return newDictionary; + } + internal override RealmCollectionBase> CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmDictionary(realm, (DictionaryHandle)handle, Metadata); internal override CollectionHandleBase GetOrCreateHandle() => _dictionaryHandle; diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index bb0b79288d..f3cee70daf 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -90,18 +90,6 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba } } - internal static RealmList CreateAndAdd(Realm realm, ListHandle handle, RealmValue content) - { - var newList = new RealmList(realm, handle, null); - - foreach (var item in content.AsList()) - { - newList.Add(item); - } - - return newList; - } - #region implementing IList members public void Add(T value) @@ -211,6 +199,18 @@ internal RealmResults ToResults() return new RealmResults(Realm, resultsHandle, Metadata); } + internal static RealmList CreateAndAdd(Realm realm, ListHandle handle, RealmValue content) + { + var newList = new RealmList(realm, handle, null); + + foreach (var item in content.AsList()) + { + newList.Add(item); + } + + return newList; + } + internal override RealmCollectionBase CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmList(realm, (ListHandle)handle, Metadata); protected override T GetValueAtIndex(int index) => _listHandle.GetValueAtIndex(index, Realm).As(); diff --git a/Realm/Realm/DatabaseTypes/RealmSet.cs b/Realm/Realm/DatabaseTypes/RealmSet.cs index 50f77ef88e..8933061bcf 100644 --- a/Realm/Realm/DatabaseTypes/RealmSet.cs +++ b/Realm/Realm/DatabaseTypes/RealmSet.cs @@ -127,6 +127,18 @@ internal RealmResults ToResults() return new RealmResults(Realm, resultsHandle, Metadata); } + internal static RealmSet CreateAndAdd(Realm realm, SetHandle handle, RealmValue content) + { + var newSet = new RealmSet(realm, handle, null); + + foreach (var item in content.AsList()) + { + newSet.Add(item); + } + + return newSet; + } + internal override RealmCollectionBase CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmSet(realm, (SetHandle)handle, Metadata); internal override CollectionHandleBase GetOrCreateHandle() => _setHandle; diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 928714b897..16911c78c2 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1533,8 +1533,8 @@ public bool Equals(RealmValue other) RealmValueType.Guid => AsGuid() == other.AsGuid(), RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), - RealmValueType.Set => AsSet().SequenceEqual(other.AsSet()), - RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), + RealmValueType.Set => AsSet().SetEquals(other.AsSet()), + RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), //TODO Check if this is correct RealmValueType.Null => true, _ => false, }; diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index 2f9b9da535..07b8883d58 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -48,6 +48,12 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_list_value", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr set_list_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_set_value", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_set_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); + + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_dictionary_value", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_dictionary_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_create_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_embedded_link(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); @@ -227,8 +233,28 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value var listPtr = NativeMethods.set_list_value(this, propertyIndex, out var listNativeException); listNativeException.ThrowIfNecessary(); - var listHandle = new ListHandle(Root!, listPtr); - RealmList.CreateAndAdd(realm, listHandle, value); + var handle = new ListHandle(Root!, listPtr); + RealmList.CreateAndAdd(realm, handle, value); + + return; + } + else if (value.Type == RealmValueType.Set) + { + var setPtr = NativeMethods.set_set_value(this, propertyIndex, out var setNativeException); + setNativeException.ThrowIfNecessary(); + + var handle = new SetHandle(Root!, setPtr); + RealmSet.CreateAndAdd(realm, handle, value); + + return; + } + else if (value.Type == RealmValueType.Dictionary) + { + var dictPtr = NativeMethods.set_dictionary_value(this, propertyIndex, out var dictNativeException); + dictNativeException.ThrowIfNecessary(); + + var handle = new DictionaryHandle(Root!, dictPtr); + RealmDictionary.CreateAndAdd(realm, handle, value); return; } diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index a377462bf8..c04cf4de63 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -100,6 +100,8 @@ public void List_WhenSetBeforeBeingManaged_WorksAsIntended() Assert.That(rv.AsList(), Is.EqualTo(originalList)); } + //TODO Add test about setting the list from another "managed" list + [Test] public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 8c9484fbbe..666c95b5f5 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -146,7 +146,7 @@ typedef struct realm_set { } realm_set_t; typedef struct realm_dict { - Dictionary* dictionary_ptr; + object_store::Dictionary* dictionary_ptr; } realm_dict_t; typedef struct realm_object_id { @@ -535,7 +535,7 @@ static inline realm_value_t to_capi(object_store::Set* set) return val; } -static inline realm_value_t to_capi(Dictionary* dictionary) +static inline realm_value_t to_capi(object_store::Dictionary* dictionary) { realm_value_t val{}; val.type = realm_value_type::RLM_TYPE_DICTIONARY; diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 3569488234..f2930de0a4 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -175,6 +175,30 @@ extern "C" { }); } + REALM_EXPORT object_store::Set* object_set_set_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) + { + return handle_errors(ex, [&]() { + verify_can_set(object); + + auto prop = get_property(object, property_ndx); + object.get_obj().set_collection(prop.column_key, CollectionType::Set); + + return new object_store::Set(object.realm(), object.get_obj(), prop.column_key); + }); + } + + REALM_EXPORT object_store::Dictionary* object_set_dictionary_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) + { + return handle_errors(ex, [&]() { + verify_can_set(object); + + auto prop = get_property(object, property_ndx); + object.get_obj().set_collection(prop.column_key, CollectionType::Dictionary); + + return new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); + }); + } + REALM_EXPORT Results* object_get_backlinks(Object& object, size_t property_ndx, NativeException::Marshallable& ex) { return handle_errors(ex, [&] { From 013bfb7674f48003ef81e294110844b479a0b61a Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 29 Sep 2023 10:36:49 +0200 Subject: [PATCH 28/88] Added error for sets adding collections --- Realm/Realm/DatabaseTypes/RealmSet.cs | 5 +++++ Realm/Realm/Extensions/RealmValueExtensions.cs | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/Realm/Realm/DatabaseTypes/RealmSet.cs b/Realm/Realm/DatabaseTypes/RealmSet.cs index 8933061bcf..125e3a057c 100644 --- a/Realm/Realm/DatabaseTypes/RealmSet.cs +++ b/Realm/Realm/DatabaseTypes/RealmSet.cs @@ -73,6 +73,11 @@ public bool Add(T value) var realmValue = Operator.Convert(value); + if (realmValue.Type.IsCollection()) + { + throw new InvalidOperationException("Set cannot contain other collections."); + } + AddToRealmIfNecessary(realmValue); return _setHandle.Add(realmValue); } diff --git a/Realm/Realm/Extensions/RealmValueExtensions.cs b/Realm/Realm/Extensions/RealmValueExtensions.cs index 0d5245bc73..956f848195 100644 --- a/Realm/Realm/Extensions/RealmValueExtensions.cs +++ b/Realm/Realm/Extensions/RealmValueExtensions.cs @@ -31,8 +31,17 @@ internal static class RealmValueExtensions RealmValueType.Decimal128 }; + private static readonly HashSet _collectionTypes = new() + { + RealmValueType.List, + RealmValueType.Set, + RealmValueType.Dictionary, + }; + public static bool IsNumeric(this RealmValueType type) => _numericTypes.Contains(type); + public static bool IsCollection(this RealmValueType type) => _collectionTypes.Contains(type); + public static (NativeQueryArgument[] Values, RealmValue.HandlesToCleanup?[] Handles) ToNativeValues(this QueryArgument[] arguments) { var nativeArgs = new NativeQueryArgument[arguments.Length]; From 143f27ce6257fc2a478b93b2a70b29a169a2770c Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:00:59 +0200 Subject: [PATCH 29/88] Trting to uniform access --- Realm/Realm/DatabaseTypes/RealmList.cs | 59 +++++++++++------ Realm/Realm/Handles/ListHandle.cs | 84 +++++++++++++++++++----- Realm/Realm/Handles/ObjectHandle.cs | 3 +- Realm/Realm/Helpers/CollectionHelpers.cs | 58 ++++++++++++++++ wrappers/src/list_cs.cpp | 44 ++++++++++--- 5 files changed, 202 insertions(+), 46 deletions(-) create mode 100644 Realm/Realm/Helpers/CollectionHelpers.cs diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index f3cee70daf..74d66a8dc1 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -23,6 +23,7 @@ using System.Diagnostics.CodeAnalysis; using System.Dynamic; using System.Linq.Expressions; +using System.Reflection; using System.Runtime.CompilerServices; using Realms.Dynamic; using Realms.Helpers; @@ -66,10 +67,23 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba ValidateIndex(index); var realmValue = ValidateValueToInsert(value); - if (realmValue.Type == RealmValueType.List) + if (realmValue.Type.IsCollection()) { - var newListHandle = _listHandle.SetList(index); - CreateAndAdd(Realm, newListHandle, realmValue); + switch (realmValue.Type) + { + case RealmValueType.List: + CollectionHelpers.ListCreateAndPopulate(Realm, _listHandle.SetList(index), realmValue); + break; + case RealmValueType.Set: + CollectionHelpers.SetCreateAndPopulate(Realm, _listHandle.SetSet(index), realmValue); + break; + case RealmValueType.Dictionary: + CollectionHelpers.DictionaryCreatePopulate(Realm, _listHandle.SetDictionary(index), realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } return; } @@ -96,10 +110,23 @@ public void Add(T value) { var realmValue = ValidateValueToInsert(value); - if (realmValue.Type == RealmValueType.List) + if (realmValue.Type.IsCollection()) { - var newListHandle = _listHandle.AddList(); - CreateAndAdd(Realm, newListHandle, realmValue); + switch (realmValue.Type) + { + case RealmValueType.List: + CollectionHelpers.ListCreateAndPopulate(Realm, _listHandle.AddList(), realmValue); + break; + case RealmValueType.Set: + CollectionHelpers.SetCreateAndPopulate(Realm, _listHandle.AddSet(), realmValue); + break; + case RealmValueType.Dictionary: + CollectionHelpers.DictionaryCreatePopulate(Realm, _listHandle.AddDictionary(), realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } return; } @@ -119,6 +146,10 @@ public void Add(T value) _listHandle.Add(realmValue); } + private void CreateAndPopulate() + { + } + public override int IndexOf([AllowNull] T value) { var realmValue = Operator.Convert(value); @@ -143,8 +174,8 @@ public void Insert(int index, T value) if (realmValue.Type == RealmValueType.List) { - var newListHandle = _listHandle.InsertList(index); - CreateAndAdd(Realm, newListHandle, realmValue); + var newListHandle = _listHandle.InsertCollection(index, RealmValueType.List); + CollectionHelpers.ListCreateAndPopulate(Realm, newListHandle, realmValue); return; } @@ -199,18 +230,6 @@ internal RealmResults ToResults() return new RealmResults(Realm, resultsHandle, Metadata); } - internal static RealmList CreateAndAdd(Realm realm, ListHandle handle, RealmValue content) - { - var newList = new RealmList(realm, handle, null); - - foreach (var item in content.AsList()) - { - newList.Add(item); - } - - return newList; - } - internal override RealmCollectionBase CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmList(realm, (ListHandle)handle, Metadata); protected override T GetValueAtIndex(int index) => _listHandle.GetValueAtIndex(index, Realm).As(); diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index 25aa3886e6..e0ed410229 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -32,8 +32,8 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr add_embedded(ListHandle listHandle, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_list", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr add_list(ListHandle listHandle, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_add_collection", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr add_collection(ListHandle listHandle, RealmValueType type, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_value", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(ListHandle listHandle, IntPtr targetIndex, PrimitiveValue value, out NativeException ex); @@ -41,8 +41,8 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr set_embedded(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_list", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_list(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_set_collection", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_collection(ListHandle listHandle, IntPtr targetIndex, RealmValueType type, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_value", CallingConvention = CallingConvention.Cdecl)] public static extern void insert_value(ListHandle listHandle, IntPtr targetIndex, PrimitiveValue value, out NativeException ex); @@ -50,8 +50,8 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr insert_embedded(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_list", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr insert_list(ListHandle listHandle, IntPtr targetIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_insert_collection", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr insert_collection(ListHandle listHandle, IntPtr targetIndex, RealmValueType type, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "list_get_value", CallingConvention = CallingConvention.Cdecl)] public static extern void get_value(ListHandle listHandle, IntPtr link_ndx, out PrimitiveValue value, out NativeException ex); @@ -149,13 +149,31 @@ public ObjectHandle AddEmbedded() return new ObjectHandle(Root!, result); } - public ListHandle AddList() + private IntPtr AddCollection(RealmValueType collectionType) { EnsureIsOpen(); - var listPtr = NativeMethods.add_list(this, out var nativeException); + var collectionPtr = NativeMethods.add_collection(this, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return new ListHandle(Root!, listPtr); + return collectionPtr; + } + + public ListHandle AddList() + { + var ptr = AddCollection(RealmValueType.List); + return new ListHandle(Root!, ptr); + } + + public SetHandle AddSet() + { + var ptr = AddCollection(RealmValueType.Set); + return new SetHandle(Root!, ptr); + } + + public DictionaryHandle AddDictionary() + { + var ptr = AddCollection(RealmValueType.Dictionary); + return new DictionaryHandle(Root!, ptr); } public void Set(int targetIndex, in RealmValue value) @@ -177,13 +195,31 @@ public ObjectHandle SetEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - public ListHandle SetList(int targetIndex) + private IntPtr SetCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); - var listPtr = NativeMethods.set_list(this, (IntPtr)targetIndex, out var nativeException); + var collectionPtr = NativeMethods.set_collection(this, (IntPtr)targetIndex, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return new ListHandle(Root!, listPtr); + return collectionPtr; + } + + public ListHandle SetList(int targetIndex) + { + var ptr = SetCollection(targetIndex, RealmValueType.List); + return new ListHandle(Root!, ptr); + } + + public SetHandle SetSet(int targetIndex) + { + var ptr = SetCollection(targetIndex, RealmValueType.Set); + return new SetHandle(Root!, ptr); + } + + public DictionaryHandle SetDictionary(int targetIndex) + { + var ptr = SetCollection(targetIndex, RealmValueType.Dictionary); + return new DictionaryHandle(Root!, ptr); } public void Insert(int targetIndex, in RealmValue value) @@ -205,13 +241,31 @@ public ObjectHandle InsertEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - public ListHandle InsertList(int targetIndex) + private IntPtr InsertCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); - var listPtr = NativeMethods.insert_list(this, (IntPtr)targetIndex, out var nativeException); + var collectionPtr = NativeMethods.insert_collection(this, (IntPtr)targetIndex, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return new ListHandle(Root!, listPtr); + return collectionPtr; + } + + public ListHandle InsertList(int targetIndex) + { + var ptr = InsertCollection(targetIndex, RealmValueType.List); + return new ListHandle(Root!, ptr); + } + + public SetHandle InsertSet(int targetIndex) + { + var ptr = InsertCollection(targetIndex, RealmValueType.Set); + return new SetHandle(Root!, ptr); + } + + public DictionaryHandle InsertDictionary(int targetIndex) + { + var ptr = InsertCollection(targetIndex, RealmValueType.Dictionary); + return new DictionaryHandle(Root!, ptr); } public int Find(in RealmValue value) diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index 07b8883d58..d3e48b8113 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -20,6 +20,7 @@ using System.Runtime.InteropServices; using Realms.Exceptions; using Realms.Extensions; +using Realms.Helpers; using Realms.Native; using Realms.Schema; @@ -234,7 +235,7 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value listNativeException.ThrowIfNecessary(); var handle = new ListHandle(Root!, listPtr); - RealmList.CreateAndAdd(realm, handle, value); + CollectionHelpers.ListCreateAndPopulate(realm, handle, value); return; } diff --git a/Realm/Realm/Helpers/CollectionHelpers.cs b/Realm/Realm/Helpers/CollectionHelpers.cs new file mode 100644 index 0000000000..86864dea9b --- /dev/null +++ b/Realm/Realm/Helpers/CollectionHelpers.cs @@ -0,0 +1,58 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2023 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License") +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +namespace Realms.Helpers; + +internal static class CollectionHelpers +{ + internal static RealmList ListCreateAndPopulate(Realm realm, ListHandle handle, RealmValue content) + { + var newList = new RealmList(realm, handle, null); + + foreach (var item in content.AsList()) + { + newList.Add(item); + } + + return newList; + } + + internal static RealmSet SetCreateAndPopulate(Realm realm, SetHandle handle, RealmValue content) + { + var newSet = new RealmSet(realm, handle, null); + + foreach (var item in content.AsSet()) + { + newSet.Add(item); + } + + return newSet; + } + + internal static RealmDictionary DictionaryCreatePopulate(Realm realm, DictionaryHandle handle, RealmValue content) + { + var newDict = new RealmDictionary(realm, handle, null); + + foreach (var item in content.AsDictionary()) + { + newDict.Add(item); + } + + return newDict; + } +} diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index a25b20ff54..6210d3d24a 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -83,16 +83,28 @@ REALM_EXPORT Object* list_set_embedded(List& list, size_t list_ndx, NativeExcept }); } -REALM_EXPORT List* list_set_list(List& list, size_t list_ndx, NativeException::Marshallable& ex) +REALM_EXPORT void* list_set_collection(List& list, size_t list_ndx, realm_value_type type, NativeException::Marshallable& ex) { - return handle_errors(ex, [&]() { + return handle_errors(ex, [&]()-> void*{ if (list_ndx > list.size()) { throw IndexOutOfRangeException("Insert into RealmList", list_ndx, list.size()); } - list.set_collection(list_ndx, CollectionType::List); - return new List(list.get_list(list_ndx)); + switch (type) + { + case realm::binding::realm_value_type::RLM_TYPE_LIST: + list.set_collection(list_ndx, CollectionType::List); + return new List(list.get_list(list_ndx)); + case realm::binding::realm_value_type::RLM_TYPE_SET: + list.set_collection(list_ndx, CollectionType::Set); + return new object_store::Set(list.get_set(list_ndx)); + case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + list.set_collection(list_ndx, CollectionType::Dictionary); + return new object_store::Dictionary(list.get_dictionary(list_ndx)); + default: + REALM_TERMINATE("Invalid collection type"); + } }); } @@ -132,16 +144,28 @@ REALM_EXPORT Object* list_insert_embedded(List& list, size_t list_ndx, NativeExc }); } -REALM_EXPORT List* list_insert_list(List& list, size_t list_ndx, NativeException::Marshallable& ex) +REALM_EXPORT void* list_insert_collection(List& list, size_t list_ndx, realm_value_type type, NativeException::Marshallable& ex) { - return handle_errors(ex, [&]() { + return handle_errors(ex, [&]()-> void*{ if (list_ndx > list.size()) { throw IndexOutOfRangeException("Insert into RealmList", list_ndx, list.size()); } - list.insert_collection(list_ndx, CollectionType::List); - return new List(list.get_list(list_ndx)); + switch (type) + { + case realm::binding::realm_value_type::RLM_TYPE_LIST: + list.insert_collection(list_ndx, CollectionType::List); + return new List(list.get_list(list_ndx)); + case realm::binding::realm_value_type::RLM_TYPE_SET: + list.insert_collection(list_ndx, CollectionType::Set); + return new object_store::Set(list.get_set(list_ndx)); + case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + list.insert_collection(list_ndx, CollectionType::Dictionary); + return new object_store::Dictionary(list.get_dictionary(list_ndx)); + default: + REALM_TERMINATE("Invalid collection type"); + } }); } @@ -157,9 +181,9 @@ REALM_EXPORT Object* list_add_embedded(List& list, NativeException::Marshallable }); } -REALM_EXPORT List* list_add_list(List& list, NativeException::Marshallable& ex) +REALM_EXPORT void* list_add_collection(List& list, realm_value_type type, NativeException::Marshallable& ex) { - return list_insert_list(list, list.size(), ex); + return list_insert_collection(list, list.size(), type, ex); } REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, NativeException::Marshallable& ex) From b13f4aab9fcf80ede84f35c223ab232b909875a6 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:51:48 +0200 Subject: [PATCH 30/88] Created common method for collections --- Realm/Realm/DatabaseTypes/RealmList.cs | 62 +++++++++++--------------- Realm/Realm/Handles/ListHandle.cs | 60 ++----------------------- 2 files changed, 28 insertions(+), 94 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 74d66a8dc1..2b48d6ab80 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -69,22 +69,7 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba if (realmValue.Type.IsCollection()) { - switch (realmValue.Type) - { - case RealmValueType.List: - CollectionHelpers.ListCreateAndPopulate(Realm, _listHandle.SetList(index), realmValue); - break; - case RealmValueType.Set: - CollectionHelpers.SetCreateAndPopulate(Realm, _listHandle.SetSet(index), realmValue); - break; - case RealmValueType.Dictionary: - CollectionHelpers.DictionaryCreatePopulate(Realm, _listHandle.SetDictionary(index), realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - + CreateAndPopulate(realmValue, (t) => _listHandle.SetCollection(index, t)); return; } @@ -112,22 +97,7 @@ public void Add(T value) if (realmValue.Type.IsCollection()) { - switch (realmValue.Type) - { - case RealmValueType.List: - CollectionHelpers.ListCreateAndPopulate(Realm, _listHandle.AddList(), realmValue); - break; - case RealmValueType.Set: - CollectionHelpers.SetCreateAndPopulate(Realm, _listHandle.AddSet(), realmValue); - break; - case RealmValueType.Dictionary: - CollectionHelpers.DictionaryCreatePopulate(Realm, _listHandle.AddDictionary(), realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - + CreateAndPopulate(realmValue, (t) => _listHandle.AddCollection(t)); return; } @@ -146,8 +116,28 @@ public void Add(T value) _listHandle.Add(realmValue); } - private void CreateAndPopulate() + private void CreateAndPopulate(RealmValue realmValue, Func createPtrFunc) { + var ptr = createPtrFunc(realmValue.Type); + + switch (realmValue.Type) + { + case RealmValueType.List: + var listHandle = new ListHandle(Realm.SharedRealmHandle, ptr); + CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); + break; + case RealmValueType.Set: + var setHandle = new SetHandle(Realm.SharedRealmHandle, ptr); + CollectionHelpers.SetCreateAndPopulate(Realm, setHandle, realmValue); + break; + case RealmValueType.Dictionary: + var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, ptr); + CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } } public override int IndexOf([AllowNull] T value) @@ -172,11 +162,9 @@ public void Insert(int index, T value) ValidateIndex(index); var realmValue = ValidateValueToInsert(value); - if (realmValue.Type == RealmValueType.List) + if (realmValue.Type.IsCollection()) { - var newListHandle = _listHandle.InsertCollection(index, RealmValueType.List); - CollectionHelpers.ListCreateAndPopulate(Realm, newListHandle, realmValue); - + CreateAndPopulate(realmValue, (t) => _listHandle.InsertCollection(index, t)); return; } diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index e0ed410229..d4726fb1dd 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -149,7 +149,7 @@ public ObjectHandle AddEmbedded() return new ObjectHandle(Root!, result); } - private IntPtr AddCollection(RealmValueType collectionType) + public IntPtr AddCollection(RealmValueType collectionType) { EnsureIsOpen(); @@ -158,24 +158,6 @@ private IntPtr AddCollection(RealmValueType collectionType) return collectionPtr; } - public ListHandle AddList() - { - var ptr = AddCollection(RealmValueType.List); - return new ListHandle(Root!, ptr); - } - - public SetHandle AddSet() - { - var ptr = AddCollection(RealmValueType.Set); - return new SetHandle(Root!, ptr); - } - - public DictionaryHandle AddDictionary() - { - var ptr = AddCollection(RealmValueType.Dictionary); - return new DictionaryHandle(Root!, ptr); - } - public void Set(int targetIndex, in RealmValue value) { EnsureIsOpen(); @@ -195,7 +177,7 @@ public ObjectHandle SetEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - private IntPtr SetCollection(int targetIndex, RealmValueType collectionType) + public IntPtr SetCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); @@ -204,24 +186,6 @@ private IntPtr SetCollection(int targetIndex, RealmValueType collectionType) return collectionPtr; } - public ListHandle SetList(int targetIndex) - { - var ptr = SetCollection(targetIndex, RealmValueType.List); - return new ListHandle(Root!, ptr); - } - - public SetHandle SetSet(int targetIndex) - { - var ptr = SetCollection(targetIndex, RealmValueType.Set); - return new SetHandle(Root!, ptr); - } - - public DictionaryHandle SetDictionary(int targetIndex) - { - var ptr = SetCollection(targetIndex, RealmValueType.Dictionary); - return new DictionaryHandle(Root!, ptr); - } - public void Insert(int targetIndex, in RealmValue value) { EnsureIsOpen(); @@ -241,7 +205,7 @@ public ObjectHandle InsertEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - private IntPtr InsertCollection(int targetIndex, RealmValueType collectionType) + public IntPtr InsertCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); @@ -250,24 +214,6 @@ private IntPtr InsertCollection(int targetIndex, RealmValueType collectionType) return collectionPtr; } - public ListHandle InsertList(int targetIndex) - { - var ptr = InsertCollection(targetIndex, RealmValueType.List); - return new ListHandle(Root!, ptr); - } - - public SetHandle InsertSet(int targetIndex) - { - var ptr = InsertCollection(targetIndex, RealmValueType.Set); - return new SetHandle(Root!, ptr); - } - - public DictionaryHandle InsertDictionary(int targetIndex) - { - var ptr = InsertCollection(targetIndex, RealmValueType.Dictionary); - return new DictionaryHandle(Root!, ptr); - } - public int Find(in RealmValue value) { EnsureIsOpen(); From bf9aff50112a9ffeff845adc5ec67971e107f1b1 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 2 Oct 2023 11:10:31 +0200 Subject: [PATCH 31/88] Various improvements [skip-ci] --- Realm/Realm/DatabaseTypes/RealmDictionary.cs | 48 +++++++--- Realm/Realm/DatabaseTypes/RealmList.cs | 56 ++++++------ Realm/Realm/DatabaseTypes/RealmSet.cs | 12 --- Realm/Realm/Handles/DictionaryHandle.cs | 44 ++++++++-- Realm/Realm/Handles/ObjectHandle.cs | 4 +- .../Database/RealmValueWithCollections.cs | 88 ++++++++++++++++++- wrappers/src/dictionary_cs.cpp | 50 +++++++++++ wrappers/src/list_cs.cpp | 37 +++++--- 8 files changed, 264 insertions(+), 75 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index 1e8ba2cc8a..2f37c769a8 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -65,6 +65,12 @@ public TValue this[string key] ValidateKey(key); var realmValue = Operator.Convert(value); + if (realmValue.Type.IsCollection()) + { + CreateInternalCollectionAndPopulate(realmValue, () => _dictionaryHandle.SetCollection(key, realmValue.Type)); + return; + } + if (_isEmbedded && realmValue.Type != RealmValueType.Null) { if (IsDynamic) @@ -124,6 +130,12 @@ public void Add(string key, TValue value) ValidateKey(key); var realmValue = Operator.Convert(value); + if (realmValue.Type.IsCollection()) + { + CreateInternalCollectionAndPopulate(realmValue, () => _dictionaryHandle.AddCollection(key, realmValue.Type)); + return; + } + if (_isEmbedded && realmValue.Type != RealmValueType.Null) { if (IsDynamic) @@ -225,6 +237,30 @@ private void UnsubscribeFromNotifications() _deliveredInitialKeyNotification = false; } + private void CreateInternalCollectionAndPopulate(RealmValue realmValue, Func createCollectionFunc) + { + var collectionPtr = createCollectionFunc(); + + switch (realmValue.Type) + { + case RealmValueType.List: + var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); + break; + case RealmValueType.Set: + var setHandle = new SetHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.SetCreateAndPopulate(Realm, setHandle, realmValue); + break; + case RealmValueType.Dictionary: + var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } + } + private static void ValidateKey(string key) { if (key == null) @@ -243,18 +279,6 @@ private static void ValidateKey(string key) } } - internal static RealmDictionary CreateAndAdd(Realm realm, DictionaryHandle handle, RealmValue content) - { - var newDictionary = new RealmDictionary(realm, handle, null); - - foreach (var item in content.AsList()) - { - newDictionary.Add(item); - } - - return newDictionary; - } - internal override RealmCollectionBase> CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmDictionary(realm, (DictionaryHandle)handle, Metadata); internal override CollectionHandleBase GetOrCreateHandle() => _dictionaryHandle; diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 2b48d6ab80..636b9a862b 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -69,7 +69,7 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba if (realmValue.Type.IsCollection()) { - CreateAndPopulate(realmValue, (t) => _listHandle.SetCollection(index, t)); + CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.SetCollection(index, realmValue.Type)); return; } @@ -97,7 +97,7 @@ public void Add(T value) if (realmValue.Type.IsCollection()) { - CreateAndPopulate(realmValue, (t) => _listHandle.AddCollection(t)); + CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.AddCollection(realmValue.Type)); return; } @@ -116,30 +116,6 @@ public void Add(T value) _listHandle.Add(realmValue); } - private void CreateAndPopulate(RealmValue realmValue, Func createPtrFunc) - { - var ptr = createPtrFunc(realmValue.Type); - - switch (realmValue.Type) - { - case RealmValueType.List: - var listHandle = new ListHandle(Realm.SharedRealmHandle, ptr); - CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); - break; - case RealmValueType.Set: - var setHandle = new SetHandle(Realm.SharedRealmHandle, ptr); - CollectionHelpers.SetCreateAndPopulate(Realm, setHandle, realmValue); - break; - case RealmValueType.Dictionary: - var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, ptr); - CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - } - public override int IndexOf([AllowNull] T value) { var realmValue = Operator.Convert(value); @@ -149,7 +125,7 @@ public override int IndexOf([AllowNull] T value) return -1; } - if (realmValue.Type == RealmValueType.List) + if (realmValue.Type.IsCollection()) { return -1; } @@ -164,7 +140,7 @@ public void Insert(int index, T value) if (realmValue.Type.IsCollection()) { - CreateAndPopulate(realmValue, (t) => _listHandle.InsertCollection(index, t)); + CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.InsertCollection(index, realmValue.Type)); return; } @@ -224,6 +200,30 @@ internal RealmResults ToResults() DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression expression) => new MetaRealmList(expression, this); + private void CreateInternalCollectionAndPopulate(RealmValue realmValue, Func createCollectionFunc) + { + var collectionPtr = createCollectionFunc(); + + switch (realmValue.Type) + { + case RealmValueType.List: + var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); + break; + case RealmValueType.Set: + var setHandle = new SetHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.SetCreateAndPopulate(Realm, setHandle, realmValue); + break; + case RealmValueType.Dictionary: + var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } + } + private static void ValidateIndex(int index, string name = "index") { if (index < 0) diff --git a/Realm/Realm/DatabaseTypes/RealmSet.cs b/Realm/Realm/DatabaseTypes/RealmSet.cs index 125e3a057c..600888f087 100644 --- a/Realm/Realm/DatabaseTypes/RealmSet.cs +++ b/Realm/Realm/DatabaseTypes/RealmSet.cs @@ -132,18 +132,6 @@ internal RealmResults ToResults() return new RealmResults(Realm, resultsHandle, Metadata); } - internal static RealmSet CreateAndAdd(Realm realm, SetHandle handle, RealmValue content) - { - var newSet = new RealmSet(realm, handle, null); - - foreach (var item in content.AsList()) - { - newSet.Add(item); - } - - return newSet; - } - internal override RealmCollectionBase CreateCollection(Realm realm, CollectionHandleBase handle) => new RealmSet(realm, (SetHandle)handle, Metadata); internal override CollectionHandleBase GetOrCreateHandle() => _setHandle; diff --git a/Realm/Realm/Handles/DictionaryHandle.cs b/Realm/Realm/Handles/DictionaryHandle.cs index d904eb14dc..0da75f0e2d 100644 --- a/Realm/Realm/Handles/DictionaryHandle.cs +++ b/Realm/Realm/Handles/DictionaryHandle.cs @@ -73,14 +73,20 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(DictionaryHandle handle, PrimitiveValue key, PrimitiveValue value, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set_embedded", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_embedded(DictionaryHandle handle, PrimitiveValue key, out NativeException ex); + + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set_collection", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_collection(DictionaryHandle handle, PrimitiveValue key, RealmValueType type, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_add", CallingConvention = CallingConvention.Cdecl)] public static extern void add_value(DictionaryHandle handle, PrimitiveValue key, PrimitiveValue value, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_add_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr add_embedded(DictionaryHandle handle, PrimitiveValue key, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set_embedded", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_embedded(DictionaryHandle handle, PrimitiveValue key, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_add_collection", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr add_collection(DictionaryHandle handle, PrimitiveValue key, RealmValueType type, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_contains_key", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] @@ -230,6 +236,34 @@ public void Set(string key, in RealmValue value) nativeException.ThrowIfNecessary(); } + public ObjectHandle SetEmbedded(string key) + { + EnsureIsOpen(); + + RealmValue keyValue = key; + var (primitiveKey, keyHandles) = keyValue.ToNative(); + + var result = NativeMethods.set_embedded(this, primitiveKey, out var nativeException); + keyHandles?.Dispose(); + nativeException.ThrowIfNecessary(); + + return new ObjectHandle(Root!, result); + } + + public IntPtr SetCollection(string key, RealmValueType collectionType) + { + EnsureIsOpen(); + + RealmValue keyValue = key; + var (primitiveKey, keyHandles) = keyValue.ToNative(); + + var result = NativeMethods.set_collection(this, primitiveKey, collectionType, out var nativeException); + keyHandles?.Dispose(); + nativeException.ThrowIfNecessary(); + + return result; + } + public void Add(string key, in RealmValue value) { EnsureIsOpen(); @@ -259,18 +293,18 @@ public ObjectHandle AddEmbedded(string key) return new ObjectHandle(Root!, result); } - public ObjectHandle SetEmbedded(string key) + public IntPtr AddCollection(string key, RealmValueType collectionType) { EnsureIsOpen(); RealmValue keyValue = key; var (primitiveKey, keyHandles) = keyValue.ToNative(); - var result = NativeMethods.set_embedded(this, primitiveKey, out var nativeException); + var result = NativeMethods.add_collection(this, primitiveKey, collectionType, out var nativeException); keyHandles?.Dispose(); nativeException.ThrowIfNecessary(); - return new ObjectHandle(Root!, result); + return result; } public bool ContainsKey(string key) diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index d3e48b8113..ce5cc68c79 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -245,7 +245,7 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value setNativeException.ThrowIfNecessary(); var handle = new SetHandle(Root!, setPtr); - RealmSet.CreateAndAdd(realm, handle, value); + CollectionHelpers.SetCreateAndPopulate(realm, handle, value); return; } @@ -255,7 +255,7 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value dictNativeException.ThrowIfNecessary(); var handle = new DictionaryHandle(Root!, dictPtr); - RealmDictionary.CreateAndAdd(realm, handle, value); + CollectionHelpers.DictionaryCreatePopulate(realm, handle, value); return; } diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index c04cf4de63..15fcb36551 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -37,11 +37,19 @@ private RealmValueObject PersistAndFind(RealmValue rv) return _realm.All().First(); } + #region List + [Test] public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { - var innerList1 = new List { "inner1" }; - var innerList2 = new List { "inner2", innerList1 }; + var innerList = new List { "inner2", true, 2.0 }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + var innerSet = new HashSet { 1, "str", true }; var originalList = new List { @@ -57,7 +65,9 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa new ObjectId("5f63e882536de46d71877979"), Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), new InternalObject { IntProperty = 10, StringProperty = "brown" }, - innerList2, + innerList, + innerDict, + innerSet, }; RealmValue rv = originalList; @@ -100,7 +110,34 @@ public void List_WhenSetBeforeBeingManaged_WorksAsIntended() Assert.That(rv.AsList(), Is.EqualTo(originalList)); } - //TODO Add test about setting the list from another "managed" list + [Test] + public void List_CanBeCopiedFromManagedList([Values(true, false)] bool isManaged) + { + var originalList = new List() { 1, "string", true }; + + RealmValue rv = originalList; + + if (isManaged) + { + rv = PersistAndFind(originalList).RealmValueProperty; + } + + var newObj = new RealmValueObject { RealmValueProperty = rv }; + + RealmValue rv2; + + if (isManaged) + { + rv2 = PersistAndFind(rv).RealmValueProperty; + } + else + { + rv2 = newObj.RealmValueProperty; + } + + Assert.That(rv.AsList(), Is.EqualTo(originalList)); + Assert.That(rv2.AsList(), Is.EqualTo(originalList)); + } [Test] public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) @@ -344,5 +381,48 @@ public void List_WhenManaged_WorksWithNotifications() Assert.That(callbacks.Count, Is.EqualTo(1)); } + + #endregion + + #region Set + + [Test] + public void Set_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) + { + var originalSet = new HashSet + { + RealmValue.Null, + 1, + true, + "string", + new byte[] { 0, 1, 2 }, + new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), + 1f, + 2d, + 3m, + new ObjectId("5f63e882536de46d71877979"), + Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), + new InternalObject { IntProperty = 10, StringProperty = "brown" }, + }; + + RealmValue rv = originalSet; + + if (isManaged) + { + rv = PersistAndFind(rv).RealmValueProperty; + } + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.Set)); + Assert.That(rv != RealmValue.Null); + + Assert.That(rv.AsSet(), Is.EqualTo(originalSet)); + Assert.That(rv.AsAny(), Is.EqualTo(originalSet)); + //Assert.That(rv.As>(), Is.EqualTo(originalSet)); + + Assert.That(rv == originalSet); + Assert.That(rv.Equals(originalSet)); + } + + #endregion } } diff --git a/wrappers/src/dictionary_cs.cpp b/wrappers/src/dictionary_cs.cpp index ad74e036fd..aa4ea37bda 100644 --- a/wrappers/src/dictionary_cs.cpp +++ b/wrappers/src/dictionary_cs.cpp @@ -71,6 +71,33 @@ extern "C" { }); } + REALM_EXPORT void* realm_dictionary_add_collection(object_store::Dictionary& dictionary, realm_value_t key, realm_value_type type, NativeException::Marshallable& ex) + { + return handle_errors(ex, [&]()-> void* { + + auto dict_key = from_capi(key.string); + if (dictionary.contains(dict_key)) + { + throw KeyAlreadyExistsException(dict_key); + } + + switch (type) + { + case realm::binding::realm_value_type::RLM_TYPE_LIST: + dictionary.insert_collection(dict_key, CollectionType::List); + return new List(dictionary.get_list(dict_key)); + case realm::binding::realm_value_type::RLM_TYPE_SET: + dictionary.insert_collection(dict_key, CollectionType::Set); + return new object_store::Set(dictionary.get_set(dict_key)); + case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + dictionary.insert_collection(dict_key, CollectionType::Dictionary); + return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); + default: + REALM_TERMINATE("Invalid collection type"); + } + }); + } + REALM_EXPORT void realm_dictionary_set(object_store::Dictionary& dictionary, realm_value_t key, realm_value_t value, NativeException::Marshallable& ex) { handle_errors(ex, [&]() { @@ -87,6 +114,29 @@ extern "C" { }); } + REALM_EXPORT void* realm_dictionary_set_collection(object_store::Dictionary& dictionary, realm_value_t key, realm_value_type type, NativeException::Marshallable& ex) + { + return handle_errors(ex, [&]()-> void* { + + auto dict_key = from_capi(key.string); + + switch (type) + { + case realm::binding::realm_value_type::RLM_TYPE_LIST: + dictionary.insert_collection(dict_key, CollectionType::List); + return new List(dictionary.get_list(dict_key)); + case realm::binding::realm_value_type::RLM_TYPE_SET: + dictionary.insert_collection(dict_key, CollectionType::Set); + return new object_store::Set(dictionary.get_set(dict_key)); + case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + dictionary.insert_collection(dict_key, CollectionType::Dictionary); + return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); + default: + REALM_TERMINATE("Invalid collection type"); + } + }); + } + REALM_EXPORT bool realm_dictionary_try_get(object_store::Dictionary& dictionary, realm_value_t key, realm_value_t* value, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index 6210d3d24a..f5dd4e37ca 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -195,19 +195,32 @@ REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, N if ((list.get_type() & ~PropertyType::Flags) == PropertyType::Object) { *value = to_capi(list.get(ndx), list.get_realm()); + return; } - else { - auto val = list.get_any(ndx); - if (!val.is_null() && val.get_type() == type_TypedLink) { - *value = to_capi(val.get(), list.get_realm()); - } - else if (!val.is_null() && val.get_type() == type_List) { - auto internalList = new List(list.get_list(ndx)); - *value = to_capi(internalList); - } - else { - *value = to_capi(std::move(val)); - } + + auto val = list.get_any(ndx); + + if (val.is_null()) { + *value = to_capi(std::move(val)); + return; + } + + switch (val.get_type()) { + case type_TypedLink: + *value = to_capi(val.get(), list.get_realm()); + break; + case type_List: + *value = to_capi(new List(list.get_list(ndx))); + break; + case type_Set: + *value = to_capi(new object_store::Set(list.get_set(ndx))); + break; + case type_Dictionary: + *value = to_capi(new object_store::Dictionary(list.get_dictionary(ndx))); + break; + default: + *value = to_capi(std::move(val)); + break; } }); } From 5db21f40670d16a9afd3910ad5f1518be3f6f07f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 2 Oct 2023 15:52:39 +0200 Subject: [PATCH 32/88] Fixed dictionary and added more tests --- .../Database/RealmValueWithCollections.cs | 617 +++++++++++++++++- wrappers/src/dictionary_cs.cpp | 7 +- wrappers/src/marshalling.hpp | 12 +- wrappers/src/object_cs.cpp | 38 +- 4 files changed, 632 insertions(+), 42 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 15fcb36551..cc87003cd8 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -43,13 +43,13 @@ private RealmValueObject PersistAndFind(RealmValue rv) public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; var innerDict = new Dictionary { { "s1", 1 }, { "s2", "ah" }, { "s3", true }, }; - var innerSet = new HashSet { 1, "str", true }; var originalList = new List { @@ -66,8 +66,8 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), new InternalObject { IntProperty = 10, StringProperty = "brown" }, innerList, - innerDict, innerSet, + innerDict, }; RealmValue rv = originalList; @@ -89,25 +89,38 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa } [Test] - public void List_WhenSetBeforeBeingManaged_WorksAsIntended() + public void List_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) { - var originalList = new List { 1, "string", true }; + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; - RealmValue rv = originalList; + var originalList = new List + { + innerList, + innerSet, + innerDict, + }; - var rvo = new RealmValueObject { RealmValueProperty = rv }; + RealmValue rv = originalList; - _realm.Write(() => + if (isManaged) { - _realm.Add(rvo); - }); + rv = PersistAndFind(rv).RealmValueProperty; + } - rv = rvo.RealmValueProperty; + var retrievedList = rv.AsList()[0]; + var retrivedSet = rv.AsList()[1]; + var retrievedDict = rv.AsList()[2]; - Assert.That(rv.Type, Is.EqualTo(RealmValueType.List)); - Assert.That(rv != RealmValue.Null); - - Assert.That(rv.AsList(), Is.EqualTo(originalList)); + Assert.That(retrievedList.AsList(), Is.EqualTo(innerList)); + Assert.That(retrivedSet.AsSet(), Is.EquivalentTo(innerSet)); + Assert.That(retrievedDict.AsDictionary(), Is.EquivalentTo(innerDict)); } [Test] @@ -135,6 +148,7 @@ public void List_CanBeCopiedFromManagedList([Values(true, false)] bool isManaged rv2 = newObj.RealmValueProperty; } + Assert.That(rv.AsList(), Is.EqualTo(rv2.AsList())); Assert.That(rv.AsList(), Is.EqualTo(originalList)); Assert.That(rv2.AsList(), Is.EqualTo(originalList)); } @@ -169,19 +183,17 @@ public void List_WhenManaged_IsNotSameReferenceAsOriginalList() rv = PersistAndFind(rv).RealmValueProperty; var retrievedList = rv.AsList(); - originalList.RemoveAt(1); Assert.That(ReferenceEquals(originalList, retrievedList), Is.False); } [Test] - public void ListInsideMixed_WhenUnmanaged_IsSameReferenceAsOriginalList() + public void List_WhenUnmanaged_IsSameReferenceAsOriginalList() { var originalList = new List { 1, "string", true }; RealmValue rv = originalList; var retrievedList = rv.AsList(); - originalList.RemoveAt(1); Assert.That(ReferenceEquals(originalList, retrievedList), Is.True); } @@ -317,19 +329,168 @@ public void List_AddSetInsertMoveRemoveList_WorksAsIntended() Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); } - public void List_RemoveWithListArgument_ReturnsFalse() + [Test] + public void List_AddSetInsertMoveRemoveSet_WorksAsIntended() { - var innerListVal = new List { 1, "string", true }; - var listVal = new List { innerListVal, 23 }; + var listVal = new List { 1, "string", true }; var rvo = _realm.Write(() => { return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); }); + var innerSet1 = new HashSet { "inner", 23, false }; + + // Indexer + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[1] = innerSet1; + listVal[1] = innerSet1; + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Insert + var innerSet2 = new HashSet { "inner2", 23, false }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Insert(1, innerSet2); + listVal.Insert(1, innerSet2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Add + var innerSet3 = new HashSet { "inner3", 23, false }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Add(innerSet3); + listVal.Add(innerSet3); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Move + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Move(0, 1); + listVal.Move(0, 1); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Remove _realm.Write(() => { - Assert.That(rvo.RealmValueProperty.AsList().Remove(innerListVal), Is.False); + rvo.RealmValueProperty.AsList().RemoveAt(2); + listVal.RemoveAt(2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + } + + [Test] + public void List_AddSetInsertMoveRemoveDictionary_WorksAsIntended() + { + var listVal = new List { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); + }); + + var innerDict1 = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + // Indexer + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[1] = innerDict1; + listVal[1] = innerDict1; + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Insert + var innerDict2 = new Dictionary + { + { "s1", 2 }, + { "s2", "ah" }, + { "s3", false }, + }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Insert(1, innerDict2); + listVal.Insert(1, innerDict2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Add + var innerDict3 = new Dictionary + { + { "s1", 3 }, + { "s2", "ahs" }, + { "s3", false }, + }; + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Add(innerDict3); + listVal.Add(innerDict3); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Move + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().Move(0, 1); + listVal.Move(0, 1); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + + // Remove + _realm.Write(() => + { + rvo.RealmValueProperty.AsList().RemoveAt(2); + listVal.RemoveAt(2); + }); + + Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); + } + + public void List_RemoveWithCollectionArgument_ReturnsFalse() + { + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var listVal = new List { innerList, innerSet, innerDict }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); + }); + + _realm.Write(() => + { + Assert.That(rvo.RealmValueProperty.AsList().Remove(innerList), Is.False); + Assert.That(rvo.RealmValueProperty.AsList().Remove(innerSet), Is.False); + Assert.That(rvo.RealmValueProperty.AsList().Remove(innerDict), Is.False); }); } @@ -397,7 +558,7 @@ public void Set_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMan "string", new byte[] { 0, 1, 2 }, new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), - 1f, + 1.5f, 2d, 3m, new ObjectId("5f63e882536de46d71877979"), @@ -415,14 +576,420 @@ public void Set_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMan Assert.That(rv.Type, Is.EqualTo(RealmValueType.Set)); Assert.That(rv != RealmValue.Null); - Assert.That(rv.AsSet(), Is.EqualTo(originalSet)); - Assert.That(rv.AsAny(), Is.EqualTo(originalSet)); - //Assert.That(rv.As>(), Is.EqualTo(originalSet)); + Assert.That(rv.AsSet(), Is.EquivalentTo(originalSet)); + Assert.That(rv.AsAny(), Is.EquivalentTo(originalSet)); + Assert.That(rv.As>(), Is.EquivalentTo(originalSet)); Assert.That(rv == originalSet); Assert.That(rv.Equals(originalSet)); } + [Test] + public void Set_CanBeCopiedFromManagedSet([Values(true, false)] bool isManaged) + { + var originalSet = new HashSet() { 1, "string", true }; + + RealmValue rv = originalSet; + + if (isManaged) + { + rv = PersistAndFind(rv).RealmValueProperty; + } + + var newObj = new RealmValueObject { RealmValueProperty = rv }; + + RealmValue rv2; + + if (isManaged) + { + rv2 = PersistAndFind(rv).RealmValueProperty; + } + else + { + rv2 = newObj.RealmValueProperty; + } + + Assert.That(rv.AsSet(), Is.EquivalentTo(rv2.AsSet())); + Assert.That(rv.AsSet(), Is.EquivalentTo(originalSet)); + Assert.That(rv2.AsSet(), Is.EquivalentTo(originalSet)); + } + + [Test] + public void Set_WhenBeingAddedToRealmWithInternalCollections_Throws() + { + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var errorMessage = "Set cannot contain other collections"; + + RealmValue rv = new HashSet() { 1, "string", true, innerList }; + Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), + Throws.TypeOf().And.Message.Contains(errorMessage)); + + rv = new HashSet() { 1, "string", true, innerSet }; + Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), + Throws.TypeOf().And.Message.Contains(errorMessage)); + + rv = new HashSet() { 1, "string", true, innerDict }; + Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), + Throws.TypeOf().And.Message.Contains(errorMessage)); + } + + [Test] + public void Set_WhenAddingCollections_Throws() + { + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var errorMessage = "Set cannot contain other collections"; + + var originalSet = new HashSet() { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = originalSet }); + }); + + Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerList)), + Throws.TypeOf().And.Message.Contains(errorMessage)); + + Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerSet)), + Throws.TypeOf().And.Message.Contains(errorMessage)); + + Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerDict)), + Throws.TypeOf().And.Message.Contains(errorMessage)); + } + + [Test] + public void Set_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) + { + var originalSet = new HashSet() { 1, "string", true }; + + RealmValue rvOperator = originalSet; + RealmValue rvConstructor = RealmValue.Set(originalSet); + RealmValue rvCreate = RealmValue.Create(originalSet, RealmValueType.Set); + + if (isManaged) + { + rvOperator = PersistAndFind(rvOperator).RealmValueProperty; + rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; + rvCreate = PersistAndFind(rvCreate).RealmValueProperty; + } + + Assert.That(rvOperator.AsSet(), Is.EquivalentTo(originalSet)); + Assert.That(rvConstructor.AsSet(), Is.EquivalentTo(originalSet)); + Assert.That(rvCreate.AsSet(), Is.EquivalentTo(originalSet)); + } + + [Test] + public void Set_WhenManaged_IsNotSameReferenceAsOriginalList() + { + var originalSet = new HashSet() { 1, "string", true }; + + RealmValue rv = originalSet; + rv = PersistAndFind(rv).RealmValueProperty; + var retrievedSet = rv.AsSet(); + + Assert.That(ReferenceEquals(originalSet, retrievedSet), Is.False); + } + + [Test] + public void Set_WhenUnmanaged_IsSameReferenceAsOriginalList() + { + var originalSet = new HashSet() { 1, "string", true }; + + RealmValue rv = originalSet; + var retrievedSet = rv.AsSet(); + + Assert.That(ReferenceEquals(originalSet, retrievedSet), Is.True); + } + + [Test] + public void Set_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged) + { + var stringVal = "Mario"; + var rvo = new RealmValueObject { RealmValueProperty = stringVal }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + Assert.That(rvo.RealmValueProperty == stringVal); + + var setVal = new HashSet() { 1, "string", true }; + + _realm.Write(() => + { + rvo.RealmValueProperty = setVal; + }); + + Assert.That(rvo.RealmValueProperty.AsSet(), Is.EquivalentTo(setVal)); + + var newStringVal = "Luigi"; + + _realm.Write(() => + { + rvo.RealmValueProperty = newStringVal; + }); + + Assert.That(rvo.RealmValueProperty == newStringVal); + } + + [Test] + public void Set_WhenManaged_WorksWithDynamic() + { + var originalSet = new HashSet { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.DynamicApi.Set(nameof(RealmValueObject.RealmValueProperty), originalSet); + }); + + var rvp = rvo.DynamicApi.Get(nameof(RealmValueObject.RealmValueProperty)); + + Assert.That(rvp.AsSet(), Is.EquivalentTo(originalSet)); + } + + [Test] + public void Set_WhenManaged_WorksWithNotifications() + { + var originalSet = new HashSet { 1, "string", true }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = originalSet }); + }); + + var callbacks = new List(); + using var token = rvo.RealmValueProperty.AsSet().SubscribeForNotifications((collection, changes) => + { + if (changes != null) + { + callbacks.Add(changes); + } + }); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsSet().Add("mario"); + }); + + _realm.Refresh(); + + Assert.That(callbacks.Count, Is.EqualTo(1)); + } + + #endregion + + #region Dictionary + + [Test] + public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) + { + var innerList = new List { "inner2", true, 2.0 }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + var innerSet = new HashSet { 1, "str", true }; + + var originalDict = new Dictionary + { + { "1", RealmValue.Null }, + { "2", 1 }, + { "3", true }, + { "4", "string" }, + { "5", new byte[] { 0, 1, 2 } }, + { "6", new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero) }, + { "7", 1f }, + { "8", 2d }, + { "9", 3m }, + { "a", new ObjectId("5f63e882536de46d71877979") }, + { "b", Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31") }, + { "c", new InternalObject { IntProperty = 10, StringProperty = "brown" } }, + { "d", innerList }, + { "e", innerDict }, + { "f", innerSet }, + }; + + RealmValue rv = originalDict; + + if (isManaged) + { + rv = PersistAndFind(rv).RealmValueProperty; + } + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.Dictionary)); + Assert.That(rv != RealmValue.Null); + + Assert.That(rv.AsDictionary(), Is.EquivalentTo(originalDict)); + Assert.That(rv.AsAny(), Is.EquivalentTo(originalDict)); + Assert.That(rv.As>(), Is.EquivalentTo(originalDict)); + + Assert.That(rv == originalDict); + Assert.That(rv.Equals(originalDict)); + } + + [Test] + public void Dictionary_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) + { + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var originalDict = new Dictionary + { + { "0", innerList }, + { "1", innerSet }, + { "2", innerDict }, + }; + + RealmValue rv = originalDict; + + if (isManaged) + { + rv = PersistAndFind(rv).RealmValueProperty; + } + + var retrievedList = rv.AsDictionary().ElementAt(0).Value; + var retrivedSet = rv.AsDictionary().ElementAt(1).Value; + var retrievedDict = rv.AsDictionary().ElementAt(2).Value; + + Assert.That(retrievedList.AsList(), Is.EqualTo(innerList)); + Assert.That(retrivedSet.AsSet(), Is.EquivalentTo(innerSet)); + Assert.That(retrievedDict.AsDictionary(), Is.EquivalentTo(innerDict)); + + var retrievedList2 = rv.AsDictionary()["0"]; + var retrivedSet2 = rv.AsDictionary()["1"]; + var retrievedDict2 = rv.AsDictionary()["2"]; + + Assert.That(retrievedList2.AsList(), Is.EqualTo(innerList)); + Assert.That(retrivedSet2.AsSet(), Is.EquivalentTo(innerSet)); + Assert.That(retrievedDict2.AsDictionary(), Is.EquivalentTo(innerDict)); + } + + [Test] + public void Dictionary_CanBeCopiedFromManagedDictionary([Values(true, false)] bool isManaged) + { + var originalDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + RealmValue rv = originalDict; + + if (isManaged) + { + rv = PersistAndFind(originalDict).RealmValueProperty; + } + + var newObj = new RealmValueObject { RealmValueProperty = rv }; + + RealmValue rv2; + + if (isManaged) + { + rv2 = PersistAndFind(rv).RealmValueProperty; + } + else + { + rv2 = newObj.RealmValueProperty; + } + + Assert.That(rv.AsDictionary(), Is.EqualTo(rv2.AsDictionary())); + Assert.That(rv.AsDictionary(), Is.EqualTo(originalDict)); + Assert.That(rv2.AsDictionary(), Is.EqualTo(originalDict)); + } + + [Test] + public void Dictionary_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) + { + var originalDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + RealmValue rvOperator = originalDict; + RealmValue rvConstructor = RealmValue.Dictionary(originalDict); + RealmValue rvCreate = RealmValue.Create(originalDict, RealmValueType.Dictionary); + + if (isManaged) + { + rvOperator = PersistAndFind(rvOperator).RealmValueProperty; + rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; + rvCreate = PersistAndFind(rvCreate).RealmValueProperty; + } + + Assert.That(rvOperator.AsDictionary(), Is.EqualTo(originalDict)); + Assert.That(rvConstructor.AsDictionary(), Is.EqualTo(originalDict)); + Assert.That(rvCreate.AsDictionary(), Is.EqualTo(originalDict)); + } + + [Test] + public void Dictionary_WhenManaged_IsNotSameReferenceAsOriginalList() + { + var originalDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + RealmValue rv = originalDict; + rv = PersistAndFind(rv).RealmValueProperty; + var retrievedDict = rv.AsDictionary(); + + Assert.That(ReferenceEquals(originalDict, retrievedDict), Is.False); + } + + [Test] + public void Dictionary_WhenUnmanaged_IsSameReferenceAsOriginalList() + { + var originalDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + RealmValue rv = originalDict; + var retrievedDict = rv.AsDictionary(); + + Assert.That(ReferenceEquals(originalDict, retrievedDict), Is.True); + } + #endregion } } diff --git a/wrappers/src/dictionary_cs.cpp b/wrappers/src/dictionary_cs.cpp index aa4ea37bda..7a0a1215fd 100644 --- a/wrappers/src/dictionary_cs.cpp +++ b/wrappers/src/dictionary_cs.cpp @@ -140,10 +140,11 @@ extern "C" { REALM_EXPORT bool realm_dictionary_try_get(object_store::Dictionary& dictionary, realm_value_t key, realm_value_t* value, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { - auto mixed_value = dictionary.try_get_any(from_capi(key.string)); + auto dict_key = from_capi(key.string); + auto mixed_value = dictionary.try_get_any(dict_key); if (mixed_value) { - *value = to_capi(dictionary, *mixed_value); + *value = to_capi(dictionary, *mixed_value, dict_key); return true; } @@ -160,7 +161,7 @@ extern "C" { auto pair = dictionary.get_pair(ndx); *key = to_capi(Mixed(pair.first)); - *value = to_capi(dictionary, pair.second); + *value = to_capi(dictionary, pair.second, pair.first); }); } diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 666c95b5f5..0c9a52b1a8 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -617,7 +617,7 @@ static inline realm_value_t to_capi(const Mixed& value) return val; } -inline realm_value_t to_capi(const object_store::Dictionary& dictionary, const Mixed& val) +inline realm_value_t to_capi(const object_store::Dictionary& dictionary, const Mixed& val, const StringData key) { if (val.is_null()) { return to_capi(std::move(val)); @@ -628,10 +628,18 @@ inline realm_value_t to_capi(const object_store::Dictionary& dictionary, const M if ((dictionary.get_type() & ~PropertyType::Flags) == PropertyType::Object) { return to_capi(ObjLink(dictionary.get_object_schema().table_key, val.get()), dictionary.get_realm()); } - REALM_UNREACHABLE(); case type_TypedLink: return to_capi(val.get_link(), dictionary.get_realm()); + case type_List: + return to_capi(new List(dictionary.get_list(key))); + break; + case type_Set: + return to_capi(new object_store::Set(dictionary.get_set(key))); + break; + case type_Dictionary: + return to_capi(new object_store::Dictionary(dictionary.get_dictionary(key))); + break; default: return to_capi(std::move(val)); } diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index f2930de0a4..1e3115e4fb 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -100,19 +100,33 @@ extern "C" { else { value->type = realm_value_type::RLM_TYPE_NULL; } + + return; } - else { - auto val = object.get_obj().get_any(prop.column_key); - if (!val.is_null() && val.get_type() == type_TypedLink) { - *value = to_capi(val.get(), object.realm()); - } - else if (!val.is_null() && val.get_type() == type_List) - { - *value = to_capi(new List(object.realm(), object.get_obj(), prop.column_key)); - } - else { - *value = to_capi(std::move(val)); - } + + auto val = object.get_obj().get_any(prop.column_key); + if (val.is_null()) + { + *value = to_capi(std::move(val)); + return; + } + + switch (val.get_type()) { + case type_TypedLink: + *value = to_capi(val.get(), object.realm()); + break; + case type_List: + *value = to_capi(new List(object.realm(), object.get_obj(), prop.column_key)); + break; + case type_Set: + *value = to_capi(new object_store::Set(object.realm(), object.get_obj(), prop.column_key)); + break; + case type_Dictionary: + *value = to_capi(new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key)); + break; + default: + *value = to_capi(std::move(val)); + break; } }); } From 17fb66fc4266628e24c3eef5605a53143d0ca108 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 2 Oct 2023 16:38:10 +0200 Subject: [PATCH 33/88] Small corrections --- Realm/Realm/DatabaseTypes/RealmValue.cs | 2 +- Realm/Realm/Helpers/Operator.cs | 2 +- Realm/Realm/Helpers/Operator.tt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 16911c78c2..6ee4c236ca 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1534,7 +1534,7 @@ public bool Equals(RealmValue other) RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), RealmValueType.Set => AsSet().SetEquals(other.AsSet()), - RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), //TODO Check if this is correct + RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), RealmValueType.Null => true, _ => false, }; diff --git a/Realm/Realm/Helpers/Operator.cs b/Realm/Realm/Helpers/Operator.cs index b0c94ddb9e..3059ab8cba 100644 --- a/Realm/Realm/Helpers/Operator.cs +++ b/Realm/Realm/Helpers/Operator.cs @@ -821,7 +821,7 @@ private class ISetRealmValueConverter : SpecializedConverterBase, RealmValue> { public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); - } + } #endregion ToRealmValue Converters #region FromRealmValue Converters diff --git a/Realm/Realm/Helpers/Operator.tt b/Realm/Realm/Helpers/Operator.tt index f0c87aee01..55df0e8ccc 100644 --- a/Realm/Realm/Helpers/Operator.tt +++ b/Realm/Realm/Helpers/Operator.tt @@ -361,7 +361,7 @@ namespace Realms.Helpers private class IDictionaryRealmValueConverter : SpecializedConverterBase, RealmValue> { public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); - } + } #endregion ToRealmValue Converters #region FromRealmValue Converters From 3231490463d44d7c9fb0c00d9395193895d29816 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:16:02 +0200 Subject: [PATCH 34/88] Improved tests for dictionaries --- Realm/Realm/DatabaseTypes/RealmDictionary.cs | 5 + .../Database/RealmValueWithCollections.cs | 385 +++++++++++------- 2 files changed, 241 insertions(+), 149 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index 2f37c769a8..d408be7c16 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -175,6 +175,11 @@ public bool Remove(KeyValuePair item) return false; } + if (realmValue.Type.IsCollection()) + { + return false; + } + return _dictionaryHandle.Remove(item.Key, realmValue); } diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index cc87003cd8..35b4027906 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -27,6 +27,13 @@ namespace Realms.Tests.Database [TestFixture, Preserve(AllMembers = true)] internal class RealmValueWithCollections : RealmInstanceTest { + public static Func[] CollectionGenerators = new Func[] + { + i => { return new List { $"inner{i}", i }; }, + i => { return new HashSet { $"inner{i}", i }; }, + i => { return new Dictionary { { "s1", i }, { "s2", $"ah{i}" } }; }, + }; + private RealmValueObject PersistAndFind(RealmValue rv) { _realm.Write(() => @@ -267,8 +274,8 @@ public void List_WhenManaged_CanBeModified() Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); } - [Test] - public void List_AddSetInsertMoveRemoveList_WorksAsIntended() + [TestCaseSource(nameof(CollectionGenerators))] + public void List_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func collectionGenerator) { var listVal = new List { 1, "string", true }; @@ -277,97 +284,35 @@ public void List_AddSetInsertMoveRemoveList_WorksAsIntended() return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); }); - var innerList1 = new List { "inner", 23, false }; - // Indexer - _realm.Write(() => - { - rvo.RealmValueProperty.AsList()[1] = innerList1; - listVal[1] = innerList1; - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Insert - var innerList2 = new List { "inner2", 23, false }; - - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Insert(1, innerList2); - listVal.Insert(1, innerList2); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Add - var innerList3 = new List { "inner3", 23, false }; - - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Add(innerList3); - listVal.Add(innerList3); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Move - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Move(0, 1); - listVal.Move(0, 1); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Remove - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().RemoveAt(2); - listVal.RemoveAt(2); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - } - - [Test] - public void List_AddSetInsertMoveRemoveSet_WorksAsIntended() - { - var listVal = new List { 1, "string", true }; - - var rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); - }); - - var innerSet1 = new HashSet { "inner", 23, false }; + var c1 = collectionGenerator(1); - // Indexer _realm.Write(() => { - rvo.RealmValueProperty.AsList()[1] = innerSet1; - listVal[1] = innerSet1; + rvo.RealmValueProperty.AsList()[1] = c1; + listVal[1] = c1; }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); // Insert - var innerSet2 = new HashSet { "inner2", 23, false }; + var c2 = collectionGenerator(2); _realm.Write(() => { - rvo.RealmValueProperty.AsList().Insert(1, innerSet2); - listVal.Insert(1, innerSet2); + rvo.RealmValueProperty.AsList().Insert(1, c2); + listVal.Insert(1, c2); }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); // Add - var innerSet3 = new HashSet { "inner3", 23, false }; + var c3 = collectionGenerator(3); _realm.Write(() => { - rvo.RealmValueProperty.AsList().Add(innerSet3); - listVal.Add(innerSet3); + rvo.RealmValueProperty.AsList().Add(c3); + listVal.Add(c3); }); Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); @@ -392,82 +337,6 @@ public void List_AddSetInsertMoveRemoveSet_WorksAsIntended() } [Test] - public void List_AddSetInsertMoveRemoveDictionary_WorksAsIntended() - { - var listVal = new List { 1, "string", true }; - - var rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject { RealmValueProperty = listVal }); - }); - - var innerDict1 = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; - - // Indexer - _realm.Write(() => - { - rvo.RealmValueProperty.AsList()[1] = innerDict1; - listVal[1] = innerDict1; - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Insert - var innerDict2 = new Dictionary - { - { "s1", 2 }, - { "s2", "ah" }, - { "s3", false }, - }; - - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Insert(1, innerDict2); - listVal.Insert(1, innerDict2); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Add - var innerDict3 = new Dictionary - { - { "s1", 3 }, - { "s2", "ahs" }, - { "s3", false }, - }; - - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Add(innerDict3); - listVal.Add(innerDict3); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Move - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().Move(0, 1); - listVal.Move(0, 1); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - - // Remove - _realm.Write(() => - { - rvo.RealmValueProperty.AsList().RemoveAt(2); - listVal.RemoveAt(2); - }); - - Assert.That(rvo.RealmValueProperty.AsList(), Is.EqualTo(listVal)); - } - public void List_RemoveWithCollectionArgument_ReturnsFalse() { var innerList = new List { "inner2", true, 2.0 }; @@ -990,6 +859,224 @@ public void Dictionary_WhenUnmanaged_IsSameReferenceAsOriginalList() Assert.That(ReferenceEquals(originalDict, retrievedDict), Is.True); } + [Test] + public void Dictionary_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged) + { + var stringVal = "Mario"; + var rvo = new RealmValueObject { RealmValueProperty = stringVal }; + + if (isManaged) + { + _realm.Write(() => + { + _realm.Add(rvo); + }); + } + + Assert.That(rvo.RealmValueProperty == stringVal); + + var dictVal = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + _realm.Write(() => + { + rvo.RealmValueProperty = dictVal; + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + + var newStringVal = "Luigi"; + + _realm.Write(() => + { + rvo.RealmValueProperty = newStringVal; + }); + + Assert.That(rvo.RealmValueProperty == newStringVal); + } + + [Test] + public void Dictionary_WhenManaged_CanBeModified() + { + var dictVal = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = dictVal }); + }); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["s1"] = "Mario"; + dictVal["s1"] = "Mario"; // To keep both list updated + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary().Remove("s2"); + dictVal.Remove("s2"); + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary().Add("s4", "newVal"); + dictVal.Add("s4", "newVal"); + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + } + + [TestCaseSource(nameof(CollectionGenerators))] + public void Dictionary_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func collectionGenerator) + { + var dictVal = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = dictVal }); + }); + + // Indexer + var c1 = collectionGenerator(1); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["s1"] = c1; + dictVal["s1"] = c1; + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + + // Add + var c3 = collectionGenerator(3); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary().Add("s4", c3); + dictVal.Add("s4", c3); + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + + // Remove + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary().Remove("s4"); + dictVal.Remove("s4"); + }); + + Assert.That(rvo.RealmValueProperty.AsDictionary(), Is.EquivalentTo(dictVal)); + } + + [Test] + public void Dictionary_RemoveWithCollectionArgument_ReturnsFalse() + { + var innerList = new List { "inner2", true, 2.0 }; + var innerSet = new HashSet { 1, "str", true }; + var innerDict = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var dictVal = new Dictionary + { + { "s1", innerList }, + { "s2", innerSet }, + { "s3", innerDict }, + }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = dictVal }); + }); + + _realm.Write(() => + { + Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s1", innerList)), Is.False); + Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s2", innerSet)), Is.False); + Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s3", innerDict)), Is.False); + }); + } + + [Test] + public void Dictionary_WhenManaged_WorksWithDynamic() + { + var dictVal = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.DynamicApi.Set(nameof(RealmValueObject.RealmValueProperty), dictVal); + }); + + var rvp = rvo.DynamicApi.Get(nameof(RealmValueObject.RealmValueProperty)); + + Assert.That(rvp.AsDictionary(), Is.EqualTo(dictVal)); + } + + [Test] + public void Dictionary_WhenManaged_WorksWithNotifications() + { + var dictVal = new Dictionary + { + { "s1", 1 }, + { "s2", "ah" }, + { "s3", true }, + }; + + var rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject { RealmValueProperty = dictVal }); + }); + + var callbacks = new List(); + using var token = rvo.RealmValueProperty.AsDictionary().SubscribeForNotifications((collection, changes) => + { + if (changes != null) + { + callbacks.Add(changes); + } + }); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["s2"] = "mario"; + }); + + _realm.Refresh(); + + Assert.That(callbacks.Count, Is.EqualTo(1)); + } + #endregion } } From a70b483a4c054c69b772a7fcb3104a050687d7d0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 3 Oct 2023 13:45:37 +0200 Subject: [PATCH 35/88] Small fixes --- Tests/Realm.Tests/Database/NotificationTests.cs | 4 ++-- Tests/Realm.Tests/Database/ObjectSchemaTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Realm.Tests/Database/NotificationTests.cs b/Tests/Realm.Tests/Database/NotificationTests.cs index 146849eda5..460e4ff26d 100644 --- a/Tests/Realm.Tests/Database/NotificationTests.cs +++ b/Tests/Realm.Tests/Database/NotificationTests.cs @@ -1581,7 +1581,7 @@ public void DictionaryOfObjects_SubscribeForNotifications_DoesntReceiveModificat dict.Clear(); }); - VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: false); + VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: true); } [Test] @@ -1627,7 +1627,7 @@ public void DictionaryOfPrimitives_SubscribeForNotifications_ShallowHasNoEffect( dict.Clear(); }); - VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: false); + VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: true); } private void VerifyNotifications(List notifications, diff --git a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs index d461fc5650..79b989bec2 100644 --- a/Tests/Realm.Tests/Database/ObjectSchemaTests.cs +++ b/Tests/Realm.Tests/Database/ObjectSchemaTests.cs @@ -315,7 +315,7 @@ public void Property_FromTypeGeneric_Object( [Test] public void Property_Primitive_Tests( - [ValueSource(nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, + [ValueSource(typeof(TestHelpers), nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, [ValueSource(nameof(BoolValues))] bool isPrimaryKey, [ValueSource(nameof(IndexTypes))] IndexType indexType, [ValueSource(nameof(BoolValues))] bool isNullable) @@ -367,7 +367,7 @@ public void Property_Primitive_Tests( [Test] public void Property_PrimitiveCollection_Tests( [ValueSource(nameof(CollectionTypes))] PropertyType collectionType, - [ValueSource(nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, + [ValueSource(typeof(TestHelpers), nameof(TestHelpers.PrimitiveRealmValueTypes))] RealmValueType type, [ValueSource(nameof(BoolValues))] bool isNullable) { Property getProperty() => collectionType switch From da46cb9e9949982d5678920aa605ec2566ffd81b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 3 Oct 2023 14:11:21 +0200 Subject: [PATCH 36/88] Simplified set collection on object --- Realm/Realm/DatabaseTypes/RealmValue.cs | 8 ++-- Realm/Realm/Handles/ObjectHandle.cs | 52 +++++++++---------------- wrappers/src/object_cs.cpp | 44 ++++++++------------- 3 files changed, 39 insertions(+), 65 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 6ee4c236ca..3b65d343da 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -191,7 +191,7 @@ private RealmValue(IDictionary dict) : this() /// The input list to copy. /// A new RealmValue representing the input list. /// Once created, this RealmValue will just wrap the input collection. - /// When the object containing this RealmValue gets managed, then this value will be a Realm list. + /// After the object containing this RealmValue gets managed this value will be a Realm list. public static RealmValue List(IList value) => new(value); /// @@ -200,7 +200,7 @@ private RealmValue(IDictionary dict) : this() /// The input set to copy. /// A new RealmValue representing the input set. /// Once created, this RealmValue will just wrap the input collection. - /// When the object containing this RealmValue gets managed, then this value will be a Realm set. + /// After the object containing this RealmValue gets managed this value will be a Realm set. public static RealmValue Set(ISet value) => new(value); /// @@ -209,7 +209,7 @@ private RealmValue(IDictionary dict) : this() /// The input dictionary to copy. /// A new RealmValue representing the input dictionary. /// Once created, this RealmValue will just wrap the input collection. - /// When the object containing this RealmValue gets managed, then this value will be a Realm dictionary. + /// After the object containing this RealmValue gets managed this value will be a Realm dictionary. public static RealmValue Dictionary(IDictionary value) => new(value); internal static RealmValue Create(T value, RealmValueType type) @@ -1534,7 +1534,7 @@ public bool Equals(RealmValue other) RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), RealmValueType.Set => AsSet().SetEquals(other.AsSet()), - RealmValueType.Dictionary => AsDictionary().SequenceEqual(other.AsDictionary()), + RealmValueType.Dictionary => AsDictionary().Count == other.AsDictionary().Count && !AsDictionary().Except(other.AsDictionary()).Any(), RealmValueType.Null => true, _ => false, }; diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index ce5cc68c79..d5a4e13aa1 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -46,14 +46,8 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_value", CallingConvention = CallingConvention.Cdecl)] public static extern void set_value(ObjectHandle handle, IntPtr propertyIndex, PrimitiveValue value, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_list_value", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_list_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_set_value", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_set_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); - - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_dictionary_value", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_dictionary_value(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); + [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_set_collection_value", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr set_collection_value(ObjectHandle handle, IntPtr propertyIndex, RealmValueType type, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "object_create_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr create_embedded_link(ObjectHandle handle, IntPtr propertyIndex, out NativeException ex); @@ -229,33 +223,25 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value throw new NotSupportedException($"Asymmetric objects cannot be linked to and cannot be contained in a RealmValue. Attempted to set {value} to {metadata.Schema.Name}.{propertyName}"); } } - else if (value.Type == RealmValueType.List) - { - var listPtr = NativeMethods.set_list_value(this, propertyIndex, out var listNativeException); - listNativeException.ThrowIfNecessary(); - - var handle = new ListHandle(Root!, listPtr); - CollectionHelpers.ListCreateAndPopulate(realm, handle, value); - - return; - } - else if (value.Type == RealmValueType.Set) - { - var setPtr = NativeMethods.set_set_value(this, propertyIndex, out var setNativeException); - setNativeException.ThrowIfNecessary(); - - var handle = new SetHandle(Root!, setPtr); - CollectionHelpers.SetCreateAndPopulate(realm, handle, value); - - return; - } - else if (value.Type == RealmValueType.Dictionary) + else if (value.Type.IsCollection()) { - var dictPtr = NativeMethods.set_dictionary_value(this, propertyIndex, out var dictNativeException); - dictNativeException.ThrowIfNecessary(); + var collectionPtr = NativeMethods.set_collection_value(this, propertyIndex, value.Type, out var collNativeException); + collNativeException.ThrowIfNecessary(); - var handle = new DictionaryHandle(Root!, dictPtr); - CollectionHelpers.DictionaryCreatePopulate(realm, handle, value); + switch (value.Type) + { + case RealmValueType.List: + CollectionHelpers.ListCreateAndPopulate(realm, new ListHandle(Root!, collectionPtr), value); + break; + case RealmValueType.Set: + CollectionHelpers.SetCreateAndPopulate(realm, new SetHandle(Root!, collectionPtr), value); + break; + case RealmValueType.Dictionary: + CollectionHelpers.DictionaryCreatePopulate(realm, new DictionaryHandle(Root!, collectionPtr), value); + break; + default: + break; + } return; } diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 1e3115e4fb..7469825cb3 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -177,39 +177,27 @@ extern "C" { }); } - REALM_EXPORT List* object_set_list_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) + REALM_EXPORT void* object_set_collection_value(Object& object, size_t property_ndx, realm_value_type type, NativeException::Marshallable& ex) { - return handle_errors(ex, [&]() { - verify_can_set(object); - - auto prop = get_property(object, property_ndx); - object.get_obj().set_collection(prop.column_key, CollectionType::List); - - return new List(object.realm(), object.get_obj(), prop.column_key); - }); - } - - REALM_EXPORT object_store::Set* object_set_set_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) - { - return handle_errors(ex, [&]() { - verify_can_set(object); - - auto prop = get_property(object, property_ndx); - object.get_obj().set_collection(prop.column_key, CollectionType::Set); - - return new object_store::Set(object.realm(), object.get_obj(), prop.column_key); - }); - } - - REALM_EXPORT object_store::Dictionary* object_set_dictionary_value(Object& object, size_t property_ndx, NativeException::Marshallable& ex) - { - return handle_errors(ex, [&]() { + return handle_errors(ex, [&]()-> void* { verify_can_set(object); auto prop = get_property(object, property_ndx); - object.get_obj().set_collection(prop.column_key, CollectionType::Dictionary); - return new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); + switch (type) + { + case realm::binding::realm_value_type::RLM_TYPE_LIST: + object.get_obj().set_collection(prop.column_key, CollectionType::List); + return new List(object.realm(), object.get_obj(), prop.column_key); + case realm::binding::realm_value_type::RLM_TYPE_SET: + object.get_obj().set_collection(prop.column_key, CollectionType::Set); + return new object_store::Set(object.realm(), object.get_obj(), prop.column_key); + case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: + object.get_obj().set_collection(prop.column_key, CollectionType::Dictionary); + return new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); + default: + REALM_TERMINATE("Invalid collection type"); + } }); } From 9e60c87275ee92d30b8607c4ebc57302dcbe9a5a Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 16 Oct 2023 11:07:36 +0200 Subject: [PATCH 37/88] Added test for dynamic and simplified collection generation --- .../Database/RealmValueWithCollections.cs | 252 +++++++++--------- 1 file changed, 121 insertions(+), 131 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 35b4027906..d979cd8544 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -27,11 +27,15 @@ namespace Realms.Tests.Database [TestFixture, Preserve(AllMembers = true)] internal class RealmValueWithCollections : RealmInstanceTest { + public static Func> ListGenerator = i => { return new List { $"inner{i}", i }; }; + public static Func> SetGenerator = i => { return new HashSet { $"inner{i}", i }; }; + public static Func> DictGenerator = i => { return new Dictionary { { "s1", i }, { "s2", $"ah{i}" } }; }; + public static Func[] CollectionGenerators = new Func[] { - i => { return new List { $"inner{i}", i }; }, - i => { return new HashSet { $"inner{i}", i }; }, - i => { return new Dictionary { { "s1", i }, { "s2", $"ah{i}" } }; }, + i => (RealmValue)ListGenerator(i), + i => (RealmValue)SetGenerator(i), + i => (RealmValue)DictGenerator(i), }; private RealmValueObject PersistAndFind(RealmValue rv) @@ -49,14 +53,9 @@ private RealmValueObject PersistAndFind(RealmValue rv) [Test] public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { - var innerList = new List { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var originalList = new List { @@ -98,14 +97,9 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa [Test] public void List_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) { - var innerList = new List { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var originalList = new List { @@ -133,7 +127,7 @@ public void List_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true [Test] public void List_CanBeCopiedFromManagedList([Values(true, false)] bool isManaged) { - var originalList = new List() { 1, "string", true }; + var originalList = ListGenerator(1); RealmValue rv = originalList; @@ -163,7 +157,7 @@ public void List_CanBeCopiedFromManagedList([Values(true, false)] bool isManaged [Test] public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { - var originalList = new List { 1, "string", true }; + var originalList = ListGenerator(1); RealmValue rvOperator = originalList; RealmValue rvConstructor = RealmValue.List(originalList); @@ -184,7 +178,7 @@ public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Valu [Test] public void List_WhenManaged_IsNotSameReferenceAsOriginalList() { - var originalList = new List { 1, "string", true }; + var originalList = ListGenerator(1); RealmValue rv = originalList; rv = PersistAndFind(rv).RealmValueProperty; @@ -196,7 +190,7 @@ public void List_WhenManaged_IsNotSameReferenceAsOriginalList() [Test] public void List_WhenUnmanaged_IsSameReferenceAsOriginalList() { - var originalList = new List { 1, "string", true }; + var originalList = ListGenerator(1); RealmValue rv = originalList; var retrievedList = rv.AsList(); @@ -220,7 +214,7 @@ public void List_AfterCreation_CanBeAssigned([Values(true, false)] bool isManage Assert.That(rvo.RealmValueProperty == stringVal); - var listVal = new List { 1, "string", true }; + var listVal = ListGenerator(1); _realm.Write(() => { @@ -364,9 +358,9 @@ public void List_RemoveWithCollectionArgument_ReturnsFalse() } [Test] - public void List_WhenManaged_WorksWithDynamic() + public void List_WhenManaged_WorksWithDynamicLikeApi() { - var originalList = new List { 1, "string", true }; + var originalList = ListGenerator(1); var rvo = _realm.Write(() => { @@ -383,10 +377,32 @@ public void List_WhenManaged_WorksWithDynamic() Assert.That(rvp.AsList(), Is.EqualTo(originalList)); } +#if !UNITY + [Test] +#endif + public void List_WhenManaged_WorksWithDynamic() + { + var originalList = ListGenerator(1); + + dynamic rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.RealmValueProperty = originalList; + }); + + var rvp = rvo.RealmValueProperty; + + Assert.That(rvp.AsList(), Is.EqualTo(originalList)); + } + [Test] public void List_WhenManaged_WorksWithNotifications() { - var originalList = new List { 1, "string", true }; + var originalList = ListGenerator(1); var rvo = _realm.Write(() => { @@ -404,7 +420,7 @@ public void List_WhenManaged_WorksWithNotifications() _realm.Write(() => { - rvo.RealmValueProperty.AsList()[2] = "mario"; + rvo.RealmValueProperty.AsList()[1] = "mario"; }); _realm.Refresh(); @@ -456,7 +472,7 @@ public void Set_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMan [Test] public void Set_CanBeCopiedFromManagedSet([Values(true, false)] bool isManaged) { - var originalSet = new HashSet() { 1, "string", true }; + var originalSet = SetGenerator(1); RealmValue rv = originalSet; @@ -486,14 +502,9 @@ public void Set_CanBeCopiedFromManagedSet([Values(true, false)] bool isManaged) [Test] public void Set_WhenBeingAddedToRealmWithInternalCollections_Throws() { - var innerList = new List { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var errorMessage = "Set cannot contain other collections"; @@ -513,14 +524,9 @@ public void Set_WhenBeingAddedToRealmWithInternalCollections_Throws() [Test] public void Set_WhenAddingCollections_Throws() { - var innerList = new List { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var errorMessage = "Set cannot contain other collections"; @@ -544,7 +550,7 @@ public void Set_WhenAddingCollections_Throws() [Test] public void Set_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { - var originalSet = new HashSet() { 1, "string", true }; + var originalSet = SetGenerator(1); RealmValue rvOperator = originalSet; RealmValue rvConstructor = RealmValue.Set(originalSet); @@ -565,7 +571,7 @@ public void Set_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Value [Test] public void Set_WhenManaged_IsNotSameReferenceAsOriginalList() { - var originalSet = new HashSet() { 1, "string", true }; + var originalSet = SetGenerator(1); RealmValue rv = originalSet; rv = PersistAndFind(rv).RealmValueProperty; @@ -577,7 +583,7 @@ public void Set_WhenManaged_IsNotSameReferenceAsOriginalList() [Test] public void Set_WhenUnmanaged_IsSameReferenceAsOriginalList() { - var originalSet = new HashSet() { 1, "string", true }; + var originalSet = SetGenerator(1); RealmValue rv = originalSet; var retrievedSet = rv.AsSet(); @@ -601,7 +607,7 @@ public void Set_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged Assert.That(rvo.RealmValueProperty == stringVal); - var setVal = new HashSet() { 1, "string", true }; + var setVal = SetGenerator(1); _realm.Write(() => { @@ -621,9 +627,9 @@ public void Set_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged } [Test] - public void Set_WhenManaged_WorksWithDynamic() + public void Set_WhenManaged_WorksWithDynamicLikeApi() { - var originalSet = new HashSet { 1, "string", true }; + var originalSet = SetGenerator(1); var rvo = _realm.Write(() => { @@ -640,10 +646,32 @@ public void Set_WhenManaged_WorksWithDynamic() Assert.That(rvp.AsSet(), Is.EquivalentTo(originalSet)); } +#if !UNITY + [Test] +#endif + public void Set_WhenManaged_WorksWithDynamic() + { + var originalSet = SetGenerator(1); + + dynamic rvo = _realm.Write(() => + { + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.RealmValueProperty = originalSet; + }); + + var rvp = rvo.RealmValueProperty; + + Assert.That(rvp.AsSet(), Is.EquivalentTo(originalSet)); + } + [Test] public void Set_WhenManaged_WorksWithNotifications() { - var originalSet = new HashSet { 1, "string", true }; + var originalSet = SetGenerator(1); var rvo = _realm.Write(() => { @@ -676,14 +704,9 @@ public void Set_WhenManaged_WorksWithNotifications() [Test] public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { - var innerList = new List { "inner2", true, 2.0 }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; - var innerSet = new HashSet { 1, "str", true }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var originalDict = new Dictionary { @@ -725,14 +748,9 @@ public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] boo [Test] public void Dictionary_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) { - var innerList = new List { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var originalDict = new Dictionary { @@ -768,12 +786,7 @@ public void Dictionary_WhenUsingIndexAccessWithCollections_WorkAsExpected([Value [Test] public void Dictionary_CanBeCopiedFromManagedDictionary([Values(true, false)] bool isManaged) { - var originalDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var originalDict = DictGenerator(1); RealmValue rv = originalDict; @@ -803,12 +816,7 @@ public void Dictionary_CanBeCopiedFromManagedDictionary([Values(true, false)] bo [Test] public void Dictionary_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { - var originalDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var originalDict = DictGenerator(1); RealmValue rvOperator = originalDict; RealmValue rvConstructor = RealmValue.Dictionary(originalDict); @@ -829,12 +837,7 @@ public void Dictionary_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame [Test] public void Dictionary_WhenManaged_IsNotSameReferenceAsOriginalList() { - var originalDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var originalDict = DictGenerator(1); RealmValue rv = originalDict; rv = PersistAndFind(rv).RealmValueProperty; @@ -846,12 +849,7 @@ public void Dictionary_WhenManaged_IsNotSameReferenceAsOriginalList() [Test] public void Dictionary_WhenUnmanaged_IsSameReferenceAsOriginalList() { - var originalDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var originalDict = DictGenerator(1); RealmValue rv = originalDict; var retrievedDict = rv.AsDictionary(); @@ -875,12 +873,7 @@ public void Dictionary_AfterCreation_CanBeAssigned([Values(true, false)] bool is Assert.That(rvo.RealmValueProperty == stringVal); - var dictVal = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var dictVal = DictGenerator(1); _realm.Write(() => { @@ -902,12 +895,7 @@ public void Dictionary_AfterCreation_CanBeAssigned([Values(true, false)] bool is [Test] public void Dictionary_WhenManaged_CanBeModified() { - var dictVal = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var dictVal = DictGenerator(1); var rvo = _realm.Write(() => { @@ -942,12 +930,7 @@ public void Dictionary_WhenManaged_CanBeModified() [TestCaseSource(nameof(CollectionGenerators))] public void Dictionary_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func collectionGenerator) { - var dictVal = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var dictVal = DictGenerator(1); var rvo = _realm.Write(() => { @@ -989,14 +972,9 @@ public void Dictionary_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; - var innerDict = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var innerList = ListGenerator(1); + var innerSet = SetGenerator(1); + var innerDict = DictGenerator(1); var dictVal = new Dictionary { @@ -1019,14 +997,9 @@ public void Dictionary_RemoveWithCollectionArgument_ReturnsFalse() } [Test] - public void Dictionary_WhenManaged_WorksWithDynamic() + public void Dictionary_WhenManaged_WorksWithDynamicLikeApi() { - var dictVal = new Dictionary - { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + var dictVal = DictGenerator(1); var rvo = _realm.Write(() => { @@ -1043,15 +1016,32 @@ public void Dictionary_WhenManaged_WorksWithDynamic() Assert.That(rvp.AsDictionary(), Is.EqualTo(dictVal)); } +#if !UNITY [Test] - public void Dictionary_WhenManaged_WorksWithNotifications() +#endif + public void Dictionary_WhenManaged_WorksWithDynamic() { - var dictVal = new Dictionary + var dictVal = DictGenerator(1); + + dynamic rvo = _realm.Write(() => { - { "s1", 1 }, - { "s2", "ah" }, - { "s3", true }, - }; + return _realm.Add(new RealmValueObject()); + }); + + _realm.Write(() => + { + rvo.RealmValueProperty = dictVal; + }); + + var rvp = rvo.RealmValueProperty; + + Assert.That(rvp.AsDictionary(), Is.EqualTo(dictVal)); + } + + [Test] + public void Dictionary_WhenManaged_WorksWithNotifications() + { + var dictVal = DictGenerator(1); var rvo = _realm.Write(() => { From cf6387fea2b2f5237ec7fe08054c834338309e45 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:37:56 +0200 Subject: [PATCH 38/88] Updated db --- .../Realm.Tests/EmbeddedResources/v6db.realm | Bin 1835008 -> 1835008 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Realm.Tests/EmbeddedResources/v6db.realm b/Tests/Realm.Tests/EmbeddedResources/v6db.realm index c48dd2ec8fe0006efdb32c5e4ad932850b689035..92537ee080965dd92af987c9321bb0ca2f58897e 100644 GIT binary patch delta 12824 zcmc&)YiwKBeLt6P(WFF4Bt=@3cxg)w^>D*EFEvQl>pDq^EnQhVM)@JFs5ors)RvjZ z*f^jA3A8}Dw!@g*Y-YPHP=|fchdo$iSh=AeI3O6b2R3!UHUVo<2I~L~7zPgLFnuWC z{eI`3L(#IG#7Tf1>CgW?um9`(&;LB`Z8WNlMzy~CF(K3bqYi%d%#)v-8580hl3$3x zEhp9Q7mtb1lP-`{bS8yxS#IPFg zqVS}`QwmQj991}`a9rUTg_S@-Awq9aF+&(VVwb{hg+0JatnjJ=pTd5H10cwHYT~wk zEjnX=KN@-1f=bGWK@6*K3`t|&3FD}b0PKeAGsX$q2uvHM{hVpmpAzDPA+}>X@lL$I zaIi2~$hvZ_HJ1gS(ek^*xaDye(6(g2RF}W+0UILRbo37gNQZQR3?Ta}?qu9@p3gxR z%88UsR+11hZVx=36X;?rJLUGeFJ}Lz+f1QpDCcXtEstB|eFiL7@Fk|m1UhCtCW?36 z_A{QH&vWk3wbh%F~$r)Q2-C=hx@?xyf3~2jeIqC!`+f*OC&)~YG$e7fEr9)&7ZZ&HjV*Y5NoYSO}em<7}7w zPsrW}Q-|SZct#}V4cIj>9+^NZB)ecnrzuXu)pB*EEn6X!zxiYTKNDaUwI|r2w zng=E@JeUafYI?4I?#d?@&z?W8%i$GI(!9)*Ffr)53=yH#?oiKh7^j?cy`ZbxF%NoQ z)$}M|$zSz1IR>I2>Djg5LTXf1G6ZKg$BW7C22%k_eyv&&Z~idYHdfHwyR~uk{Iyrg zg{vFo3+1cVFT7ITx&;e}KgYUERHIFKJ9#4;zvw1|lrkrchST@9N7m}g!}+xKPcKM&7_ zNF_1TI@8A}*q!u@{lDQ@0D>7bm>nH&uafDlTaNvdRFlM*Yf=kzG_GvsUqsgZnmc9x zbleQkq6}qkrY35x5cL8Tne4$#^a7ZMcKyv{05jxpQhEqn1xp|jNrS)p`uWZ4*FU{^ zy?k-=x-O@AuWWL#oOYn&+Mi*LZazz{aAT%A$uQ4xW&LAQi}tf-#D2~jvA;aE#%PF1 zzRhSVSyH66On#~}H6|(<1NN(C_QsC1*4m<-uN~eA_hfhw(wO9BDLQZmmti7<#B%cK zPhl8T9u$MA$;me#nqD%JmMVmxE=TTLb-4l{@_)kqW;B54pR*Uz5&LIRDKg?-acVnR z^jGGx^CA<-9ApNWLFh0f!fQz@nNL={+e7!)EGcqgJUhNEL+(I2?5N17$ME#Vi%~~p zNabR^?hlbY|M*|RelwPNhLYmHLBr@^&$i@In`%x4q@sH<-FMV%xrzKlRT{j+YGIr4 zbjIEC%FI3DWZtk#@h8SOQ#h&XAGcdIs8jK4S{@5N;8@I*-oCkM|s&oE*M= z_ksJ6!z{8>x|znPN1$ybbe^(2`q7{J=vkC&@>gzbT-NpAeOm~j2?|OXwwGb^%5YwX zDWA4Yz&3qR`)V=}vJej0n#$-X$VljcKNxl|okAvX5v`;Qu%aPDgCYf)-3QUm*cznP zJTF}w@}vica;aTc+czz`DF%RE>JXt6$^M7QIXA|bxDK(oW;500?+8}G23y9+cXNQ%eAicqXPN# zu6bI97SQ8|N12%C$Q)++W5rpg9u?A~(cZ0`WazYh?opW75tY~H zS__;qt=Vl59cd3wb6L~~J9LS$L6jIfbcwM;mlzv#iLpVK*st0lJ_luiK5fZx3xY}j zUq3hq7BL}&*psYT4tG9r`P}-}_4Dgn<^1~A#xr;ZlrOGtT?1@9yS}w~3Hi^hZ>_Jd zZ|%US?8kX>D0gc^%hWZakRjR&`BWiQOqEiVR5evgHB!w~E5(i$7!s2)XmNPn5hyR) zR5vbPVLw=w{g~?VcZlo93*J4i_tu&a8DPvki()UlGrlY5G=}7kfJC3+P^tF5L^vC^ z>T=z!&7oXYXbge~N!!D63k2EmZjlZG^+x)Insc@A&Rk4%=jb!Nxjjk6p-&%!Aac5r zp7cLHXa*XCpuuto5~?L~o_4}3c4kO0pD4;m=@7)2diEwyU<*H?H48N|mOl|ZA+kZX z&jl^zRHVn?M$8y{`O1y@uY!@B9-1*V9GxbVmtur#6bFM1+>fA!fO z^AOnA_YeGSx&Jaxv#lzhaEr=pZ+>t7=&> zBr~jc?JEn|h4c&#Q&^YkJfxBj*h=|ya|Z5P%pfT_LY)~iSuhcj;q}<_dJ`udjdgtv zM$iXD|HuJR*Vn*a4~W+p5D1SXSID2vL_)OE7k>Yw{q)!WI>yP>O%#S3JeVoz z7lBiymF%gtQ?1GTslus#@*p`#?gYijz{$wT#!7RgwbEYMUfEgctaMj8q1_e9WT-|U zUYW_oWqC*};OP)Lg;A?@m3}GD7Gb>ez6H%FQoVNUnc&G?Q3RPAk_S zMcY^MReepE3F|qCK=b`{s%}ARHaI>@pscU&8~C`9M;dM^IZb5=eiY$oXENZ)&2OiB zQ>FRHYHqbN+e`N6+Wzg;mU$3Ae4v}{E;X0MgKU3nn%iGWj!B`p#eKqS2Eo@|o2{iA zv-Y_85xLfadygy)g)-_zX?+KpTd)n&&I>23&{ zZTqawsEh4tn@uVlEIko7)!Q-y}S8)AomaLHZ7nCci*`4b9<6ds-YFesKpA@q{y zJ+>sWK9|DeP`jS7qg{_U$3qkvcuI@lj>HixLxIDQaE5yKfs7eWy#))h9XyJ=i@F9k z`XiLRsEI_9rL+y%K0BWP<2K!myOANxiSse;= z{F%{@?Z_uPy*6z{vEjxdZrERrn)bu)mCuIy2nL7q6C!}U#h8dJ^iNVNr&}5FwnxG? z$hfRUSv|DdK7tGr*}xs5-!)6FdgfB$$#c&>wO+XJnNMAK>f%MG$?$hV$rRdM*o_Kz zTI8XnTNGTT!&?&bfSF-Beb^qxG>b{B?~SbM=t<6MxG0xcvkdZYB4 z*uOgaSXRR}pv(2-Evu<;IrN)G7E}dE-W<{K{z2`ucg&6;6Zo^+NfTYge|mu3X<77F__#Ye%{3SIfi9 zrLFQSSl&8tAKLL#SiJJ^($CAGQ9ng>vUot{*VGi#9&%ol@2idDK;gwvB6v20r@;lw zu;2Dfd$^Wh2BZ!1yO!nR(=VDz1YCvP-nFUnN)TbCoq8>SS2b@d$x2baoq->D;QL1| zY$5nEz84lbz&gGga=@TDBIE6BizHL~{W*O@&1+34h=K`>9Z{c|lPy>{pKn{`@{uZnuDIXf2lUdDLQc7HE_41sP3d{>{Bjc3s&?V7KnjU@r)<<> zZMY+StOH980F_6C_^}J)|KU1W=i$g>_hT`P{|9k0uG6&GVE5h@t162LahE5@|5)Kv zj+#t6Ss$h{AH`Gzy#3BjcCq1Vy91u0x8`kOf||arZ_VHK@A|vJYN#IygbU%l@BsHt z`{U+BXYz2ezBW~v>fsGdV!9UP9=ILjUNRr=#)~t%cr8^)bQ8GG$9w8#vX|`4=5fbX zoa@7Fb8y`4v@mmK%N&@IOdAf|UN~H+E*7)RY&V--sxEc0$I34^ml+54S8ygiQ-n*4 z#~061QN)3%ixs#Gy_3Tb0VHnz_ZP;d?W-RNjB&~ntefY4VoWFb$QHUKu}QH1@}cRu zDxT=!qmV_Oy;?Vof1jSdx%$hVz!~Os8xzk2QesDbx z@~*EjpTZVgzav%-s_v*9m9Ymf^o@$1sm0%nibGt>R)Mir*74Hc(HX&1G*H+Aj3#yj zAvnbc|5yWKfR~$aUcfeh{AJn)Q!=9Ky&r6;eqb=h0km3Ckp~n2ML-Eq0aO7s02BFG z;D28i4zz?ma@;T0FCoa7-e^LQ`o0OX;{gW*(E+CP9EvWU`+(8Jjt~GRN0-Gm$G#Oq zzxVuRO23$TNj`j#Z^byKIh@J!0Oy6VBXa50V?(N5ljrcNJW@xmDXJQIEh-WK6OaYu z0BZmXkOve1ML-Eq0aO7sKm*VOv;Z`Vr4Fb*V2n$hKoi{q_*vaOD`njW{&EfUM{Xy3 z$Rg;DOFSv+&cplpa*dudfNcY(u*5=pzPCC6){Zm4vt{4uI9U2!hKa^V^0E+~gs^w# z*8v>R8qb`7!0ShZ5_qPm8FfZKlF)P3B4H%-$BmQ_CA6=o{wzoR@McPm4=-|9K|wHH zOwI=tM}$H-5GVku0G@)Hz*^H5p0@#`Nzn_!Na_;kuHW_*1V8Sh54@*u`_PVrX_@Ra z)?WZ_9EWM}dep8*Kd!z3kD%a5F-G3Ilv-V=7F^r6!2X6R`fxN zGE8I$M;^Uw{eQn}rHy+Kb`P-jZ8`@h0Fs3a1rG2#tf3tnC~X2-suuQ>t-c5!?x2i` zy+GF8Re4#c%F$lhoBl@7?db~0Gu7qq2nM;lykjdJBiL5?2NZ{0mFFNZ!MSAWtMbEd z^M)&iJ)XK!yZM7(@x<$Rt%@Mz@`?w=2;kgIkmnt0quk-qVZR|C(SU~r% zNOrM&G!P0ZaQqgWp$Qu%e#bEz6n&6Ksa*_s8-nZt_;QBRo!{lP(I5e>3;fKbjfW1H z@~zSypb8-h*k(5Ydw>Aq^$via*0vyQ5mU7T;N>nq!z;q@2Y?(xL19XD5e(MDh$QKVA-H$MMi8zr$6d= z-*+!BMXB~M^bjAabo@LLZSIFP;j+RjJ&Uoj%wN9%~2xYfDcel6F=xOKZ1@EF)o(t&mM_wcIX$<(E zpfe%WLN{Bj8@;hFGGZpk;k_Q6*J(n02q&4()Qz@4g7sNAmY{%g= zd%&f%JRkVQQ;t*1{z?B}kb2Xpl|VHxA6N*mY@)m+_jk(OT($yZo#<>9)C#l%p`ad= zrC_EXl0Ai>Nva50366c5Q{M@09#IKagY&@!TNM%n-mp{elrX{X2^<&a#IM|ZKll&I zFv9H%#v)3u4Xt21xX{<^$@-HLlyN+0hG$Kop zm56Ld9CKTd^@vml7BaBblSpilKTsH`4>Sg3E264BT6O6p+XKV4*Xps*&YPLR8P5qg zdGLypx@;^CP7GFKwHU1>dzbTw6;_m6eo#1Ge1B+%{KinC&r|Rh^3~j0O4j>Ry1y2! zW65>57_?6A1asv5@ewCvfr+@x45sC5{OGI#I{-UTKBJfm+6r~`nTgNGoAISM+e00} zflfY!{%i4#{${5h-0-&I?YKxd@1zJG-D*EjP#n(&=$27t!3+QH{XSlPJe0M zXBZ+GO&ZBUvY4!^4YjFKaVpT(N=YgpE6K4j%;Md(%e!foO0t@qPc9@m;BK=lC7u4V z=uvk=qHi&|oLo(|>;asikzBK9paA&F_t$w+PQKHZIm3xJ$g~*Lu@^gKA7YR5(M}vk zLG@T`X92rA%Ur2XUVi?I=-`#jAyq!HH@asEmC?LSU@_qk+3xIa13_mS2q-~?+eGg= za<^ZfyTG~&i?f+fTh?PKc|ecswQUL(^gBpwq^f>LQ+qaacBT2HF_;=r(8Esb4G;L~ zMkW@i^9+W|b9)N?u*#JrWPRF*vd3wdKG$C!xsCax5m1LsXu}3KS0IAom{?CK*r5xu z?eodZP{@b*6j{sJ+nGJmt}qOBmJb|)Na#ES8`!d^kyiotke%Fe$npT@L0dz<0Q_Hv zEe~TJxNYQ%kW)c_j_09l8p8VQx6*wvp4_3GLQDb+yM$P`zI#nge`XG2I9`8Q71Kb$ zQibums%XB6x@)SK{3BJgehT^A!~xoYC3w_?8>RtFA$Teg)7InH)zpp(9*aPV$C052MVH{D0(g_5-^yF?X9c zx;t%{@E9jXfWvge$~RSzr6ig|P?I*GvUa&z zVWSsQ!8LYr%l2gkH7t)qr7g?Gdl`e40XCak)&R;Ii>@`K?!_W-$BSv7bCtAb!{2}Z z?SFNzmgpD2VkvU5T&4;2SPg-5W|@MPi~Y`q;?kVUg!f9)a~_yLc@p5VsiC|GH!lM# zKnqv{NOx+@tpY=_+P~G833`{krmuZgmUF2>SNXdc2WNQ4hH-txE$aOtO=g0-`LEC|5~aXm>*gUECpHvYXhN_K4c6Pv!!e`HV`bOGV5C6&dg3Af}N-teCqk_-+6xO^VO*@uxxH&2k07eZ#oNk+)9CI z#AO-hje*-L;}^n;FhI$ofwnlx*m6S1al@SzZM?+^bes|wY}0A!vR)c0237}Z{h>%S zvYKk8CiQ5xqMO-?Y$Z3HTMn!QRs+kym0+`fUf1m_&d;aFQ>th{<1Ke>TalknVAnF| z?rxvD^4eJPG2GApUt$IJ@?M9ngZOmVW;NxnjcreGef;bjf##X^%U7@LPZZ$dNnrB} zgXUSvQXwy27|xGriFJhdZrvtRo4Y}eyLF2WvfC`p6V7{r%iyTM85sRY+bCmC?z90%aa%Q{aVZ)$MzBqc3entcy>MH7Og1l!Dy6F}Eh8&W73I?TOrVGfLLQlY zI%(BEQO9KM;%KlmqKqiRiman@_>LNv-+5ZK{`g%jE~`@+YdWGu_V}6OZ*P9lcQzMTHrn5h%n^I0d?ke`N)hXUyFHv3%==&sYt-n#<^H z<`_)H?!J&gC)@Nhb{R|PEoZlP%dH#TvL1cg`sjvwQKdkRbCP+MFOt}UclqsCO6yB^ zlrp#DBbWorww!AXtmfD9>-qK$k&R}JY%yzQ%h^h{nw`tmvQ2$aU(%^)K!^wEU0u#S zS+tsAtsm}H2TJe&y+9Y6`&<_r{p+7N7dQ@sw{>eFuS8}2{8*gs#`Yj)5Ze&26t`ae z1c9ktG`5exX`IZDQBDL#mHU+4sI~MvY8D&CD4zFsVAbD67!eTfojW!8?MiC6iO)3k zJvb}&3h_C}h8)p^J21eO3vG_mh>TLSKDeMHWbuM3o6qkJI}E|Wa=Fv z^N-j{y9)CjQjEGvkH(JoYI?xc6)&lduGIE`_4pn20lcuucPq(Za~}lkM>r1%alWG^ z*I%#kOPT2P zzHOP$nz*fI23%7ps&I6}oRc)iTKh0T4(+uqvP?zO<7AMnKJq%S8s0GD(t;e#QzTUc|Mx*NEz&tG}-O zs?0pKOEGO5)W>{X8w_R1x}azYOYPIfWVCWbHZKMf=H1oai*KF>sH$>kv)?m?qX_9S z@Zx?Pq8*J%Xli`xxtCP0B6pm4>DEU+?JecYc-nD70X%>g@Bw}x00e;$&_2bT`{+^KQP zP966LY=HKS+u-FzlpYktSMaWa55+6MD;*c`%%mg^z``G!;ckEjUPKmwWg>_*569N5 zhhA1kt>3(+;=K)*l@c%y@G4)$Rlb5VxeCky0#|t*;QJP@^-%-MFOSs)tnS3;I#8ZV zv;pw+;WDj0i}G{e^iM-#@jD6LHU-c1b?8Xlqc!J!tNf;RQZ!qdDEy0NpOoTbP1KG< zi;b7Cv3cl#IfV&C0q&+m12CWz6heZ1wxcptGpMjq00cqYzqsHSoIGyRf$@E_JKA! zYd~8!+>?0j4LNDSHipvdU;=rxgB9e6oE>`hZTw?Mta^iDz6#%TLe0Jg&;l6lIWgkUPS{`^JtS)cFV{ZAaS>Tj!B95?m5-KF&*TyM9}nuszZ z?S5(Ds^_u5m0t!Jc7~igz&gV2hMRtNL%#T;YQNet>VjPW!Z$*W z#lPL%#txfkng^Kcg#GzNsaVgwrzB=s%Ro;9TZzCr2GZ<+&l3Ri6<`^dR5l2}SawO^ zb7o`T7El6Ak3)nq@)8`~FSKS>|0@&5twpv)5ltH zrArms4%LsDn9rpOgF-X_=nUF??+?^nS_qFOU;uo!iDQkg_a$bFoobFM09Am`L>ZXu zfD)$xa)&pB>U&O`JqzmDm!EOTmm1)H!rj6fmomrrF&3eG8h`HZ;m(tj^HVHaoSe@N z-0!$7Qw0B=#Am0~sqIXG#)lN^nV)FMG?!fM_izaRzACOlNm9dv4 Date: Tue, 17 Oct 2023 10:42:51 +0200 Subject: [PATCH 39/88] Updated changelog [skip-ci] --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4a21ffa01..9d13162292 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ ## vNext (TBD) ### Enhancements +* Added support for collections of `RealmValue` (`IList`, `ISet`, `IDictionary`) to be contained in a `RealmValue`. Lists and dictionaries can contain an arbitrary number of collections themselves, while sets cannot. It is possible to convert an existing collection to a `RealmValue` using the new static methods `RealmValue.List`, `RealmValue.Set` and `RealmValue.Dictionary` or using the implicit operators if converting from common types like `List`, `HashSet` or `Dictionary`. Finally, it is possible to obtain the contained collections by using the new conversion method `AsList`, `AsSet` and `AsDictionary`. For example: + + ```csharp + var list = new List { 1, true, "stringVal" }; + + var rvo = realm.Write(() => + { + return realm.Add(new RealmValueObject { RealmValueProperty = list}); + }); + + var retrievedList = rvo.RealmValueProperty.AsList(); + ``` + (PR [#3441](https://github.com/realm/realm-dotnet/pull/3441)) + * Added support for customizing the ignore attribute applied on certain generated properties of Realm models. The configuration option is called `realm.custom_ignore_attribute` and can be set in a global configuration file (more information about global configuration files can be found in the [.NET documentation](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files)). The Realm generator will treat this as an opaque string, that will be appended to the `IgnoreDataMember` and `XmlIgnore` attributes already applied on these members. The attributes must be fully qualified unless the namespace they reside in is added to a global usings file. For example, this is how you would add `JsonIgnore` from `System.Text.Json`: ``` From 956a0a15a10f93ac2957596c0f7a7edf60041a3f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:49:03 +0200 Subject: [PATCH 40/88] Updated another db --- .../ForMigrationsToCopyAndMigrate.realm | Bin 8192 -> 8192 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm b/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm index e8c0207e3e87f8ff90b5860ac3365da250ad762b..abfac9e7a2e1c4d5a2386b31abc6c45132926c0c 100644 GIT binary patch literal 8192 zcmeHLJ!~V#6@Ih3)gw6I}{VLiy@vPe1+86Zr%sC!)Tne$hDURC=}FRH`)*i|t9YJKw&|a|HE0v|nU) zv;*oGuCx;M2hiCL743boV4pSy&@qqIetpoZe0_{5baA1#JFRxD)BBbU4#%PB`Gc6h z6noHbB=HPoBm=Z^UJO=MZ4=tzsc~c>e_!i#8?7dq`s`G+$N22fzLJz>OUYJ}kg|Pa z+w9l=j2$V9?CrYjsu_r(A6OUJqTF0#p-@=mqRi#>OG{N(s zZb)#P13$hW8Z2R2?;`(*coGhS zKg41G6^j#=zJMB%vJ-181*}pE)s|iR3i)XK#PPyD zSyW|pp;?*V%?AQAAOHD}@BRJzaDL=l`dOpX?JYKO@_A)h758Pe)v1M3@tJ;J={0)4 zJ2Xx1^H%d{sUi7kqtmM&R_^k5@1)W=UK;;-_w#12)~R)Sjph;Jebs3k9pS8cS*_Pj zDx#is8nxyjx1KJkh{wD9=({DobEEcb8H;Zw3&Dzqbw=mrd`-_NBAI6KG@NMIxUlyN!@@ns( z_|=QgzS?{FGG?&OIIxV)delLk`N&^|Nd}qtaI|aB2+tHt3KT^*2{XCnEeb$z7W=rbU)Sfzl6y;h5nWA03>p%7S zPTBP&pXT}$(&e4De)&Ra<_Q+_IZ94b{nVMx zYqab81##Jo2aC#u)rI3Zzlr0abtC?A>fAE(>5Va+(r4z{nZeQ9ZBYPJb^;D#LlGDd?TGn(saWjwnL_PtRC|ml;cEgL2JRI4M z^Xs03s@M*x+u_f;_J;5HG5yA05ZN0^ew3Vh7v7~e@UFa(cjHaGsWNjzse#Ti1%Ax|F%}`bq~TDI{GSG^sgTyxJP?Mo^W26>%3)tVrc^A1F}L(oHJU| zj(IRi&(bA*3zqeh?ciH~Uzq!p-#w3fH$?a2D9Le;eIJ?qW7d!1(0+OU#Mg)Y9M^h! z(EaAcLAO>s(3cL|YgpzyPPT-&+7EPlZto(O?()&zXVNb8ckU-8PM>?3W0nj3e4ZKo> z&)_%rR~)~I%LMU5_<5fjyGLX`7GK=#e}13GCyeQNuKzQ9M=_EubgDpP7lD;g&rpAa zwgl=J$wTgF4}E=0c5t`Wt)e}(`_2@1>P2Rp$!GoC?pkU6_I6$&mxALH>@`!{$7=pddEaLiD*uxLMi7|&*10>!^1F7aL8whPXsGfIr` zMSt!M)5A%q`)b-yxmliRG2(R1_V?Eo?mU8=GZ-DDD!nu)(P!eN^Fb`ZI z#&huKs{!ZL_Q1J8Y%`h7W_&?e#_7X=I1!-ipq5d80`*~>aXu%BSu(_02bcTcvh-vb zXPnPG);z>I0M2XR97O!%&e|XbRto**%iVcrcpU*4@q!PzK~3Qw0UyEm0leF78G?(E zIz4gDdbX$-c|K=E(E}`7q7Yk*ODJ13w7=FY|uH_x3(87bHL}2;Yl9 zSb}7JY=PMwyoXU;qm6&-VqWV?hPkZX7xaVpwx6&FfB6$?lv+m&W?jZO1kJjDPq{>$ sqW(0BGf?0F@2j$6V8y_SffWNQ238EL7+5i|VqnFa5Q4;urmTZB?)NZ7RA1J!)Dn& z@l!TeT8SM_^c}|d3q;map+&T>;(Xu)RMy-gF zG#~!)ZpH&g`NZq{iGWCXb+|uN2*twE7hb#F8+f7LQ38|VGhoMYVg=$?fx}17{{AMG zC>)F}zROSesfznH4yj&Uy+kaf$j6-VIlthS{ED9)E+%J(WS{ap=L^`PrSg!!BKUYI zywW^mb2y@+b3De_S9L|aG7C43S3G4mhebsC%$d`%iqo zGj{tb3+lhS?2jbHpV`msxk;6%PUvH-NIi+(#x>>ZO5;W!lqd33PUX3LBWH3hFXW}X zk_#1U+z-ccsW^&{zL&&_iuX2;Fa;p|_Jd!3d`9}kZw?20If3=47lhr8m-X*@-|Ow?&OaVM9)y12kHg*ocdKXyy}do$ zRom^Z-}e~b2zvgYL!X{n1&SLumgD=vVd-~B@-^gLEIuH`v@d~?YtGC|llD;ro)(sP zQX~p|1hX8JF*}#*mx^`WFK(v4Fspg}tX~_}&eLre`uEQRVFGuTPsI$qRW+vgj;`f3 zwm|<<)pprRF_xnq_UhUyg{96TF4V1Mad%`LSrZFt%0FnDMvmW7_ZsC>$9_taILbBL z5><5n)L_&yXOy6><%34E``wMWP9?7|t%|*3 zr~e<2Z|O4UWogZA@fJQ(gpb^jrXaqC@cO#I_IWBkPMEwR>s#$1honS`E&q2qmipi{s*^FkP z3F{V4a&yqe`g)TFKeC7U1oPQa@H%r`r|!^C$iz>{&>)HP9eD#h-KqH_L& Date: Tue, 17 Oct 2023 12:15:14 +0200 Subject: [PATCH 41/88] Updated db --- .../ForMigrationsToCopyAndMigrate.realm | Bin 8192 -> 8192 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm b/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm index abfac9e7a2e1c4d5a2386b31abc6c45132926c0c..2b30bff65389205936dc006c399507744220c525 100644 GIT binary patch delta 2173 zcmZ{mO=ufO6vyA}toBR0(pXlVO)QeFn}CX25VZ?B_^>TWtCUjIl7bHnRT=`KAK(xl zbhD14?Lq4F;9J$34uvfA5=ak%UKW}{jxHocM->{yp@)hdlKy9RmBb;L(d_Qb&dmG0 z|9i7`H|&OiRi>m^%jx~X>u29yv5DS7@rX3(dqG)s&Jh{09+(}gVOR4#SN^0`20LeS zj7T@-Rc(22%gNuktxhxs)0B z1TUWS@x+E2PX=YAZw1(n9~yNwgmVk7CTp?%WZ7*~y1B6qWjU0seP`SnToCe(5xfy% zBQ+n(*yX1TU_We6rW(rjbH{vc2veS9D}&9l9=f49ct4PzaI2!6f#THA`g-8j+@{$w zo4lp%m>p9NP2Zouv#x1V55X|0@5D~(oSYl)6(|XSvI3f%ylqxM10@d4t@0vJ{HZ|6 zrdgI}t*X7BEF4)RJ^fUY1&k)v)H;+uSm#Tz)z=p4@LV({F~?AB`QplVKpHwmX_WZ!$X8dl|_)#bR}vg2}RVFKYAO-l*~}l zT_~ALm6Un!LwVY*?6p0PJTjAjT|umeTGvxMW&z~=%!=wik7}HRo9IPUi}A-)8C{|% z0(RXya}kV>4=j06V(~@xCdy@$#lFv~kl`Lt;a|I*S&ti`IL9{ zsUGJ-Bp|sxcjSelWh(n>UX26=R71W6sDkRa?obn}`)pj?NpwP9Lj0RI$I)*?weB4v zhj8jgc)>!V2yYKmcm&aL`20_?{CUYo?ng8s!7NQ-jCfxk>L~y)v{m__xU#qEQ<#AS zEQBVO7HNX-0BFjqKvLSo#-TA!KBi%zUJOOFlp2CUt51U|tpw0~=;Y(ModDNywWyDN zhvsCl5j6{>r=T@jy?6KC4E4|1Y*x6Jd9aYMT1dI4;H|=Fdr8iU$M;SsXu*yGAH|K# z6gcv36hbi-of4pVrsjz!&`*Gm;szOTpLzg}spmBdj~=Da8+2kanN4Q+J)Aw9sL-kx z3+Tsj2k;?UtweMx5!?4OsYmR25~s`&zV|l@Q;1w&&Uk%zi>gxadD5n=eaKtWVz9=p zT8A$Xk_5sLmL_FgNcIpJONk?awTk*75zN6b1}HA#)C zKGGv31`e)qV9brc943EnmFA6n(^Woni_uc2R4-rNz4oaxhh$j`Rovi(N(g~tw2x<% zDsA-Zs4+%;JgXT2?WK+#P>U^`8{>1i4QJGbaBz*51ga`nK})KpJ=`%OAV0OT`O4oL iNGNr#l)rWSC(%PYKzjvq07z48%5YE0&*w|O=Klw2%yEDK delta 1812 zcmZuyJ&YSg6n?w2-q}C<=3?h?ba!MUPQobSbWu*og^N9O7fg}JLKH@dhzU920D=>w zFan7!PgFi0g-c7M*bbvKClLu36fQtA5=A6}c5x^OtAd)sAW1FpNQjf4F7RXHs*Bl48idQ{ z?km_A%n^7;=BO?W%F$TPWCo(KePCwZzZn<#J5z08C>9Q+J@W9le)ogV-n)1BMYfM5 zkIc91#HgE-9EW0;ZwiOMk`CVyH#}v8&cqpdy?`Hz9o`BQ-!~Np)|V`U%>aZfmsGwl zBZCK4O*<$|+6|*+^inQ!n}ghNS5JN=A8j!r3Yh8XMit^F(C zmx>?TA^#z-y<15$(P>0PIo(4Gp;V}y&!Pz$`(vXPRD_)q8MT*rx> zW0vll=UPtJT&5<{)cCghjU>PjmHA)yz&dq@F4w%**1B%b?YoIPa7XSVcj8XnnLBs+ z*sEE0Klx~zM_zli;~oq0GV((6<30a}VB*cZCW;U4KG%XW-xKSX7KItvTBzsNy%E&- zh3Jo{ zf|wnu7+p$K=7&SIk{^%%8jr`sN|o{>0L@@!a9rmrYVGS%n<7L1@cO0_N1|f&tx;}d z&+M){^oD*M^n$V8rWUv*jTz$6Xboy&R83iQF{e@nbLvv&5B$>P@3a*PfYnXMS!&P@ zikS#a@HvK^!ZtOp4~uP7PqWgTGDZWLacT!~(9GANMxxGm6`chu%_(CPf=9-w3tg0v z5Ma_gzze{Tf@ZTB|EP-R6{d^`Qwld5tOdK63Irn03N*>h?2Jqs;?yBdbMy?=_yuXN;Ix{Ye2(%ke)~#2nlVj>PGsbpKPT2bbOW=d1G7%OWh$L^ Date: Tue, 17 Oct 2023 12:35:48 +0200 Subject: [PATCH 42/88] Updated changelog --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d13162292..f5fb10c8c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,13 +23,16 @@ (Issue [#2579](https://github.com/realm/realm-dotnet/issues/2579)) ### Fixed -* None +* Fixed serveral smaller issues when logging into with a single user when using multiple auth providers. (Core 13.21.0) +* Fixed an excepton when querying over a geospatial dataset that some objects with a type property set to something other than `Point` (case insensitive). Those objects are now just ignored. (Core 13.21.0) +* Fixed an issue that would make the user in an inconstisten state if the user was logged out while an access toekn refresh was in progress. (Core 13.21.0) +* Fixed an issue where Android.bp builds would fail with SSL certificat validation errors because the trusted CA roots bundle was not included. (Core 13.23.0) ### Compatibility * Realm Studio: 13.0.0 or later. ### Internal -* Using Core 13.20.1. +* Using Core 13.23.0 ## 11.5.0 (2023-09-15) From 59549966d395e2462b7c329dd54d0e117615584b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:52:26 +0200 Subject: [PATCH 43/88] Test --- Tests/Realm.Tests/Database/RealmValueWithCollections.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index d979cd8544..daa48d3a7e 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -735,14 +735,14 @@ public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] boo } Assert.That(rv.Type, Is.EqualTo(RealmValueType.Dictionary)); - Assert.That(rv != RealmValue.Null); + Assert.That(rv != RealmValue.Null, "Different than null"); Assert.That(rv.AsDictionary(), Is.EquivalentTo(originalDict)); Assert.That(rv.AsAny(), Is.EquivalentTo(originalDict)); Assert.That(rv.As>(), Is.EquivalentTo(originalDict)); - Assert.That(rv == originalDict); - Assert.That(rv.Equals(originalDict)); + Assert.That(rv == originalDict, "Equal to than original dict"); + Assert.That(rv.Equals(originalDict), "Equal to original dict with Equals"); } [Test] From 2fdea52cb4537544486984b023848af6867ecae0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 18 Oct 2023 10:42:33 +0200 Subject: [PATCH 44/88] Fix to generated files --- .../AsymmetricTestClass_generated.cs | 2 +- .../EmbeddedTestClass_generated.cs | 2 +- .../JustForObjectReference_generated.cs | 2 +- .../RootRealmClass_generated.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricTestClass_generated.cs b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricTestClass_generated.cs index f2e0d9dbd3..1b7691e102 100644 --- a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricTestClass_generated.cs +++ b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/AsymmetricTestClass_generated.cs @@ -37,7 +37,7 @@ public partial class AsymmetricTestClass : IAsymmetricObject, INotifyPropertyCha Realms.IRealmAccessor Realms.IRealmObjectBase.Accessor => Accessor; - internal IAsymmetricTestClassAccessor Accessor => _accessor ??= new AsymmetricTestClassUnmanagedAccessor(typeof(AsymmetricTestClass)); + private IAsymmetricTestClassAccessor Accessor => _accessor ??= new AsymmetricTestClassUnmanagedAccessor(typeof(AsymmetricTestClass)); /// [IgnoreDataMember, XmlIgnore] diff --git a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedTestClass_generated.cs b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedTestClass_generated.cs index d194d39073..97b733a3eb 100644 --- a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedTestClass_generated.cs +++ b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/EmbeddedTestClass_generated.cs @@ -37,7 +37,7 @@ public partial class EmbeddedTestClass : IEmbeddedObject, INotifyPropertyChanged Realms.IRealmAccessor Realms.IRealmObjectBase.Accessor => Accessor; - internal IEmbeddedTestClassAccessor Accessor => _accessor ??= new EmbeddedTestClassUnmanagedAccessor(typeof(EmbeddedTestClass)); + private IEmbeddedTestClassAccessor Accessor => _accessor ??= new EmbeddedTestClassUnmanagedAccessor(typeof(EmbeddedTestClass)); /// [IgnoreDataMember, XmlIgnore] diff --git a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/JustForObjectReference_generated.cs b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/JustForObjectReference_generated.cs index 7443657274..53d74958d4 100644 --- a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/JustForObjectReference_generated.cs +++ b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/JustForObjectReference_generated.cs @@ -37,7 +37,7 @@ public partial class JustForObjectReference : IRealmObject, INotifyPropertyChang Realms.IRealmAccessor Realms.IRealmObjectBase.Accessor => Accessor; - internal IJustForObjectReferenceAccessor Accessor => _accessor ??= new JustForObjectReferenceUnmanagedAccessor(typeof(JustForObjectReference)); + private IJustForObjectReferenceAccessor Accessor => _accessor ??= new JustForObjectReferenceUnmanagedAccessor(typeof(JustForObjectReference)); /// [IgnoreDataMember, XmlIgnore] diff --git a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs index 3b15193961..763b63f5f4 100644 --- a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs +++ b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs @@ -46,7 +46,7 @@ public partial class RootRealmClass : IRealmObject, INotifyPropertyChanged, IRef Realms.IRealmAccessor Realms.IRealmObjectBase.Accessor => Accessor; - internal IRootRealmClassAccessor Accessor => _accessor ??= new RootRealmClassUnmanagedAccessor(typeof(RootRealmClass)); + private IRootRealmClassAccessor Accessor => _accessor ??= new RootRealmClassUnmanagedAccessor(typeof(RootRealmClass)); /// [IgnoreDataMember, XmlIgnore] From 9dfc21c6dc60f09420b91c052933fb40d2cd0474 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 18 Oct 2023 13:09:43 +0200 Subject: [PATCH 45/88] Test --- Realm/Realm/DatabaseTypes/RealmValue.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 3b65d343da..8afde40059 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1534,12 +1534,25 @@ public bool Equals(RealmValue other) RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), RealmValueType.Set => AsSet().SetEquals(other.AsSet()), - RealmValueType.Dictionary => AsDictionary().Count == other.AsDictionary().Count && !AsDictionary().Except(other.AsDictionary()).Any(), + RealmValueType.Dictionary => CompareDicts(this, other), RealmValueType.Null => true, _ => false, }; } + private bool CompareDicts(RealmValue first, RealmValue second) + { + var firstDict = first.AsDictionary(); + var secondDict = second.AsDictionary(); + + if (firstDict.Count != secondDict.Count) + { + return false; + } + + return firstDict.OrderBy(kvp => kvp.Key).SequenceEqual(secondDict.OrderBy(kvp => kvp.Key)); + } + /// /// Compares two instances for equality. /// From 4a4c6d1f41a2289e5571aa8f1e2b7a064621093d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 23 Oct 2023 11:40:52 +0200 Subject: [PATCH 46/88] Small improvement --- Realm/Realm/DatabaseTypes/RealmValue.cs | 15 +-------------- Realm/Realm/Extensions/CollectionExtensions.cs | 5 +++++ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 8afde40059..5eff2b0cac 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1534,25 +1534,12 @@ public bool Equals(RealmValue other) RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), RealmValueType.Set => AsSet().SetEquals(other.AsSet()), - RealmValueType.Dictionary => CompareDicts(this, other), + RealmValueType.Dictionary => AsDictionary().DictionaryEquals(other.AsDictionary()), RealmValueType.Null => true, _ => false, }; } - private bool CompareDicts(RealmValue first, RealmValue second) - { - var firstDict = first.AsDictionary(); - var secondDict = second.AsDictionary(); - - if (firstDict.Count != secondDict.Count) - { - return false; - } - - return firstDict.OrderBy(kvp => kvp.Key).SequenceEqual(secondDict.OrderBy(kvp => kvp.Key)); - } - /// /// Compares two instances for equality. /// diff --git a/Realm/Realm/Extensions/CollectionExtensions.cs b/Realm/Realm/Extensions/CollectionExtensions.cs index 5f78c3d5cc..f18eaf3fc3 100644 --- a/Realm/Realm/Extensions/CollectionExtensions.cs +++ b/Realm/Realm/Extensions/CollectionExtensions.cs @@ -562,6 +562,11 @@ public static void PopulateCollection(ICollection source, ICollection t public static void PopulateCollection(IDictionary source, IDictionary target, bool update, bool skipDefaults) => PopulateCollectionCore(source, target, update, skipDefaults, kvp => kvp.Value); + internal static bool DictionaryEquals(this IDictionary first, IDictionary second) + { + return first.Count == second.Count && first.All(fkv => second.TryGetValue(fkv.Key, out RealmValue sVal) && fkv.Value == sVal); + } + private static bool ShouldWaitForSync(WaitForSyncMode mode, Subscription? oldSub, Subscription newSub) { switch (mode) From 452924d1cf350237d60b1f3eaba14c54fa20f683 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:07:14 +0100 Subject: [PATCH 47/88] Added field offset --- Realm/Realm/DatabaseTypes/RealmValue.cs | 41 ++++++++++++++++++------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 5eff2b0cac..277c127690 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -57,19 +57,38 @@ namespace Realms /// [Preserve(AllMembers = true)] [DebuggerDisplay("Type = {Type}, Value = {ToString(),nq}")] + [StructLayout(LayoutKind.Explicit)] public readonly struct RealmValue : IEquatable { + [FieldOffset(0)] + private readonly RealmValueType type; + + [FieldOffset(4)] + private readonly ObjectHandle? _objectHandle; + + [FieldOffset(12)] + private readonly IntPtr _propertyIndex; + + [FieldOffset(20)] private readonly PrimitiveValue _primitiveValue; + + [FieldOffset(20)] private readonly string? _stringValue; + + [FieldOffset(20)] private readonly byte[]? _dataValue; + + [FieldOffset(20)] private readonly IRealmObjectBase? _objectValue; + [FieldOffset(20)] private readonly IList? _listValue; + + [FieldOffset(20)] private readonly ISet? _setValue; - private readonly IDictionary? _dictionaryValue; - private readonly ObjectHandle? _objectHandle; - private readonly IntPtr _propertyIndex; + [FieldOffset(20)] + private readonly IDictionary? _dictionaryValue; /// /// Gets the stored in this value. @@ -82,11 +101,11 @@ namespace Realms /// type of the integral value stored in a field. /// /// The of the current value in the database. - public RealmValueType Type { get; } + public RealmValueType Type => type; internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? handle = default, IntPtr propertyIndex = default) : this() { - Type = primitive.Type; + type = primitive.Type; _objectHandle = handle; _propertyIndex = propertyIndex; @@ -122,37 +141,37 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? private RealmValue(byte[] data) : this() { - Type = RealmValueType.Data; + type = RealmValueType.Data; _dataValue = data; } private RealmValue(string value) : this() { - Type = RealmValueType.String; + type = RealmValueType.String; _stringValue = value; } private RealmValue(IRealmObjectBase obj) : this() { - Type = RealmValueType.Object; + type = RealmValueType.Object; _objectValue = obj; } private RealmValue(IList list) : this() { - Type = RealmValueType.List; + type = RealmValueType.List; _listValue = list; } private RealmValue(ISet set) : this() { - Type = RealmValueType.Set; + type = RealmValueType.Set; _setValue = set; } private RealmValue(IDictionary dict) : this() { - Type = RealmValueType.Dictionary; + type = RealmValueType.Dictionary; _dictionaryValue = dict; } From 0620eb6642ae775814313ffe3c09f04de487e245 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 31 Oct 2023 13:23:48 +0100 Subject: [PATCH 48/88] Working version --- Realm/Realm/DatabaseTypes/RealmValue.cs | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 277c127690..ed73fa0820 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -60,36 +60,36 @@ namespace Realms [StructLayout(LayoutKind.Explicit)] public readonly struct RealmValue : IEquatable { - [FieldOffset(0)] - private readonly RealmValueType type; - - [FieldOffset(4)] - private readonly ObjectHandle? _objectHandle; - - [FieldOffset(12)] - private readonly IntPtr _propertyIndex; - - [FieldOffset(20)] - private readonly PrimitiveValue _primitiveValue; - - [FieldOffset(20)] + [FieldOffset(24)] private readonly string? _stringValue; - [FieldOffset(20)] + [FieldOffset(24)] private readonly byte[]? _dataValue; - [FieldOffset(20)] + [FieldOffset(24)] private readonly IRealmObjectBase? _objectValue; - [FieldOffset(20)] + [FieldOffset(24)] private readonly IList? _listValue; - [FieldOffset(20)] + [FieldOffset(24)] private readonly ISet? _setValue; - [FieldOffset(20)] + [FieldOffset(24)] private readonly IDictionary? _dictionaryValue; + [FieldOffset(0)] + private readonly PrimitiveValue _primitiveValue; + + [FieldOffset(32)] + private readonly IntPtr _propertyIndex; + + [FieldOffset(40)] + private readonly ObjectHandle? _objectHandle; + + [FieldOffset(48)] + private readonly RealmValueType type; + /// /// Gets the stored in this value. /// From 47acf8b4a2cf193c749278c37d753ef6ade80bba Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 31 Oct 2023 13:52:58 +0100 Subject: [PATCH 49/88] Small renaming --- Realm/Realm/DatabaseTypes/RealmValue.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index ed73fa0820..5e8a93b1be 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -88,7 +88,7 @@ namespace Realms private readonly ObjectHandle? _objectHandle; [FieldOffset(48)] - private readonly RealmValueType type; + private readonly RealmValueType _type; /// /// Gets the stored in this value. @@ -101,11 +101,11 @@ namespace Realms /// type of the integral value stored in a field. /// /// The of the current value in the database. - public RealmValueType Type => type; + public RealmValueType Type => _type; internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? handle = default, IntPtr propertyIndex = default) : this() { - type = primitive.Type; + _type = primitive.Type; _objectHandle = handle; _propertyIndex = propertyIndex; @@ -141,37 +141,37 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? private RealmValue(byte[] data) : this() { - type = RealmValueType.Data; + _type = RealmValueType.Data; _dataValue = data; } private RealmValue(string value) : this() { - type = RealmValueType.String; + _type = RealmValueType.String; _stringValue = value; } private RealmValue(IRealmObjectBase obj) : this() { - type = RealmValueType.Object; + _type = RealmValueType.Object; _objectValue = obj; } private RealmValue(IList list) : this() { - type = RealmValueType.List; + _type = RealmValueType.List; _listValue = list; } private RealmValue(ISet set) : this() { - type = RealmValueType.Set; + _type = RealmValueType.Set; _setValue = set; } private RealmValue(IDictionary dict) : this() { - type = RealmValueType.Dictionary; + _type = RealmValueType.Dictionary; _dictionaryValue = dict; } From 78271b6fc54a780ddfb528cbfca914a2a49f80c7 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:17:28 +0100 Subject: [PATCH 50/88] Reordering --- Realm/Realm/DatabaseTypes/RealmValue.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 5e8a93b1be..8d66c0cc16 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -60,6 +60,9 @@ namespace Realms [StructLayout(LayoutKind.Explicit)] public readonly struct RealmValue : IEquatable { + [FieldOffset(0)] + private readonly PrimitiveValue _primitiveValue; + [FieldOffset(24)] private readonly string? _stringValue; @@ -78,16 +81,13 @@ namespace Realms [FieldOffset(24)] private readonly IDictionary? _dictionaryValue; - [FieldOffset(0)] - private readonly PrimitiveValue _primitiveValue; + [FieldOffset(24)] + private readonly ObjectHandle? _objectHandle; [FieldOffset(32)] private readonly IntPtr _propertyIndex; [FieldOffset(40)] - private readonly ObjectHandle? _objectHandle; - - [FieldOffset(48)] private readonly RealmValueType _type; /// From 08cbb78591adfe536a151fbb453ae24c4f583bf2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Nov 2023 12:58:45 +0100 Subject: [PATCH 51/88] Working version --- Realm/Realm/DatabaseTypes/RealmValue.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 8d66c0cc16..abacda09a7 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -84,10 +84,10 @@ namespace Realms [FieldOffset(24)] private readonly ObjectHandle? _objectHandle; - [FieldOffset(32)] + [FieldOffset(8)] private readonly IntPtr _propertyIndex; - [FieldOffset(40)] + [FieldOffset(16)] private readonly RealmValueType _type; /// From a833eb6ab9a56fc8b45adb6fa2834d4d47feca1b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:15:22 +0100 Subject: [PATCH 52/88] Solution 1 --- Realm/Realm/DatabaseTypes/RealmValue.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index abacda09a7..1750339e93 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -63,6 +63,13 @@ namespace Realms [FieldOffset(0)] private readonly PrimitiveValue _primitiveValue; + // This occupies the same memory space as PrimitiveValue.Type + [FieldOffset(16)] + private readonly RealmValueType _type; + + // Object fields cannot be at the same offset as non-object fields, + // thus all the following values cannot overlap _primitiveValue + // even though some are mutually exclusive [FieldOffset(24)] private readonly string? _stringValue; @@ -87,9 +94,6 @@ namespace Realms [FieldOffset(8)] private readonly IntPtr _propertyIndex; - [FieldOffset(16)] - private readonly RealmValueType _type; - /// /// Gets the stored in this value. /// @@ -107,7 +111,6 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? { _type = primitive.Type; _objectHandle = handle; - _propertyIndex = propertyIndex; switch (Type) { @@ -137,6 +140,13 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? _primitiveValue = primitive; break; } + + // This cannot be moved before setting _primitiveValue, otherwise it will overwrite _propertyIndex, due to the fact + // that they share memory space. + if (_type == RealmValueType.Int) + { + _propertyIndex = propertyIndex; + } } private RealmValue(byte[] data) : this() From b01ef4f1b4af7595023fa7d8f55d148c70300724 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Nov 2023 15:58:31 +0100 Subject: [PATCH 53/88] Final version of the RealmValue explicit layout --- Realm/Realm/DatabaseTypes/RealmValue.cs | 30 +++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 1750339e93..e66ad17f2f 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -18,7 +18,6 @@ using System; using System.Buffers; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; @@ -63,13 +62,18 @@ namespace Realms [FieldOffset(0)] private readonly PrimitiveValue _primitiveValue; - // This occupies the same memory space as PrimitiveValue.Type + // This is only used when PrimitiveValue.Type == Int. int_value in PrimitiveValue occupies the first 8 bytes + // so we can safely place this at the 9th byte. + [FieldOffset(8)] + private readonly IntPtr _propertyIndex; + + // This occupies the same memory space as PrimitiveValue.Type. [FieldOffset(16)] private readonly RealmValueType _type; // Object fields cannot be at the same offset as non-object fields, // thus all the following values cannot overlap _primitiveValue - // even though some are mutually exclusive + // even though some are mutually exclusive. [FieldOffset(24)] private readonly string? _stringValue; @@ -88,12 +92,10 @@ namespace Realms [FieldOffset(24)] private readonly IDictionary? _dictionaryValue; + // This is only used when PrimitiveValue.Type == Int. [FieldOffset(24)] private readonly ObjectHandle? _objectHandle; - [FieldOffset(8)] - private readonly IntPtr _propertyIndex; - /// /// Gets the stored in this value. /// @@ -110,7 +112,6 @@ namespace Realms internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? handle = default, IntPtr propertyIndex = default) : this() { _type = primitive.Type; - _objectHandle = handle; switch (Type) { @@ -136,17 +137,18 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? Argument.NotNull(realm, nameof(realm)); _dictionaryValue = primitive.AsDictionary(realm!); break; + case RealmValueType.Int: + _primitiveValue = primitive; + + // _propertyIndex and _objectHandler are used only when converting the RealmValue to a RealmInteger. + // _propertyIndex needs to be set after _primitiveValue due to the RealmValue struct explicit layout. + _propertyIndex = propertyIndex; + _objectHandle = handle; + break; default: _primitiveValue = primitive; break; } - - // This cannot be moved before setting _primitiveValue, otherwise it will overwrite _propertyIndex, due to the fact - // that they share memory space. - if (_type == RealmValueType.Int) - { - _propertyIndex = propertyIndex; - } } private RealmValue(byte[] data) : this() From dca0b9776e5bf1ccaab79596e7c34656fc9ac9fb Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:15:37 +0100 Subject: [PATCH 54/88] Added changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index db225ed798..24c3da5a67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ ``` (PR [#3441](https://github.com/realm/realm-dotnet/pull/3441)) +* Reduced memory usage of `RealmValue`. (PR [#3441](https://github.com/realm/realm-dotnet/pull/3441)) * Added `User.Changed` event that can be used to notify subscribers that something about the user changed - typically this would be the user state or the access token. (Issue [#3429](https://github.com/realm/realm-dotnet/issues/3429)) * Added support for customizing the ignore attribute applied on certain generated properties of Realm models. The configuration option is called `realm.custom_ignore_attribute` and can be set in a global configuration file (more information about global configuration files can be found in the [.NET documentation](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files)). The Realm generator will treat this as an opaque string, that will be appended to the `IgnoreDataMember` and `XmlIgnore` attributes already applied on these members. The attributes must be fully qualified unless the namespace they reside in is added to a global usings file. For example, this is how you would add `JsonIgnore` from `System.Text.Json`: From 778b1fc6bdcee9301a969a84d1cc6262b9bb1f03 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:01:03 +0100 Subject: [PATCH 55/88] Removed set from RealmValue --- Realm/Realm/DatabaseTypes/RealmValue.cs | 47 +------------------------ 1 file changed, 1 insertion(+), 46 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index e66ad17f2f..11a17f5b7d 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -86,9 +86,6 @@ namespace Realms [FieldOffset(24)] private readonly IList? _listValue; - [FieldOffset(24)] - private readonly ISet? _setValue; - [FieldOffset(24)] private readonly IDictionary? _dictionaryValue; @@ -129,10 +126,6 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? Argument.NotNull(realm, nameof(realm)); _listValue = primitive.AsList(realm!); break; - case RealmValueType.Set: - Argument.NotNull(realm, nameof(realm)); - _setValue = primitive.AsSet(realm!); - break; case RealmValueType.Dictionary: Argument.NotNull(realm, nameof(realm)); _dictionaryValue = primitive.AsDictionary(realm!); @@ -175,12 +168,6 @@ private RealmValue(IList list) : this() _listValue = list; } - private RealmValue(ISet set) : this() - { - _type = RealmValueType.Set; - _setValue = set; - } - private RealmValue(IDictionary dict) : this() { _type = RealmValueType.Dictionary; @@ -225,15 +212,6 @@ private RealmValue(IDictionary dict) : this() /// After the object containing this RealmValue gets managed this value will be a Realm list. public static RealmValue List(IList value) => new(value); - /// - /// Gets a RealmValue representing a set. - /// - /// The input set to copy. - /// A new RealmValue representing the input set. - /// Once created, this RealmValue will just wrap the input collection. - /// After the object containing this RealmValue gets managed this value will be a Realm set. - public static RealmValue Set(ISet value) => new(value); - /// /// Gets a RealmValue representing a dictionary. /// @@ -264,7 +242,6 @@ internal static RealmValue Create(T value, RealmValueType type) RealmValueType.Guid => Guid(Operator.Convert(value)), RealmValueType.Object => Object(Operator.Convert(value)), RealmValueType.List => List(Operator.Convert>(value)), - RealmValueType.Set => Set(Operator.Convert>(value)), RealmValueType.Dictionary => Dictionary(Operator.Convert>(value)), _ => throw new NotSupportedException($"RealmValueType {type} is not supported."), }; @@ -532,17 +509,6 @@ public IList AsList() return _listValue!; } - /// - /// Returns the stored value as a set. - /// - /// Thrown if the underlying value is not of type . - /// A set representing the value stored in the database. - public ISet AsSet() - { - EnsureType("Set", RealmValueType.Set); - return _setValue!; - } - /// /// Returns the stored value as a dictionary. /// @@ -844,8 +810,7 @@ public T As() RealmValueType.ObjectId => Operator.Convert(AsObjectId()), RealmValueType.Guid => Operator.Convert(AsGuid()), RealmValueType.Object => Operator.Convert(AsIRealmObject()), - RealmValueType.List => Operator.Convert, T>(AsList()), - RealmValueType.Set => Operator.Convert, T>(AsSet()), + RealmValueType.List => Operator.Convert, T>(AsList()), RealmValueType.Dictionary => Operator.Convert, T>(AsDictionary()), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; @@ -872,7 +837,6 @@ public T As() RealmValueType.Guid => AsGuid(), RealmValueType.Object => AsIRealmObject(), RealmValueType.List => AsList(), - RealmValueType.Set => AsSet(), RealmValueType.Dictionary => AsDictionary(), _ => throw new NotSupportedException($"RealmValue of type {Type} is not supported."), }; @@ -942,7 +906,6 @@ public override int GetHashCode() RealmValueType.ObjectId => AsObjectId().GetHashCode(), RealmValueType.Object => AsIRealmObject().GetHashCode(), RealmValueType.List => AsList().GetHashCode(), - RealmValueType.Set => AsSet().GetHashCode(), RealmValueType.Dictionary => AsDictionary().GetHashCode(), _ => 0, }; @@ -1485,13 +1448,6 @@ public override int GetHashCode() /// A containing the supplied . public static implicit operator RealmValue(List? val) => val == null ? Null : List(val); - /// - /// Implicitly constructs a from HashSet<RealmValue>. - /// - /// The value to store in the . - /// A containing the supplied . - public static implicit operator RealmValue(HashSet? val) => val == null ? Null : Set(val); - /// /// Implicitly constructs a from Dictionary<string, RealmValue>. /// @@ -1564,7 +1520,6 @@ public bool Equals(RealmValue other) RealmValueType.Guid => AsGuid() == other.AsGuid(), RealmValueType.Object => AsIRealmObject().Equals(other.AsIRealmObject()), RealmValueType.List => AsList().SequenceEqual(other.AsList()), - RealmValueType.Set => AsSet().SetEquals(other.AsSet()), RealmValueType.Dictionary => AsDictionary().DictionaryEquals(other.AsDictionary()), RealmValueType.Null => true, _ => false, From dc4545eecdf1b5f0f3e80aec48944a8ede079778 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:11:01 +0100 Subject: [PATCH 56/88] Continuing removal of set --- Realm/Realm/DatabaseTypes/RealmDictionary.cs | 4 - Realm/Realm/DatabaseTypes/RealmList.cs | 4 - Realm/Realm/Handles/ObjectHandle.cs | 3 - Realm/Realm/Helpers/CollectionHelpers.cs | 12 - Realm/Realm/Helpers/Operator.cs | 12 - Realm/Realm/Helpers/Operator.tt | 13 - .../Database/RealmValueWithCollections.cs | 302 +----------------- 7 files changed, 7 insertions(+), 343 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index d408be7c16..d4115f10d3 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -252,10 +252,6 @@ private void CreateInternalCollectionAndPopulate(RealmValue realmValue, Func ListCreateAndPopulate(Realm realm, ListHan return newList; } - internal static RealmSet SetCreateAndPopulate(Realm realm, SetHandle handle, RealmValue content) - { - var newSet = new RealmSet(realm, handle, null); - - foreach (var item in content.AsSet()) - { - newSet.Add(item); - } - - return newSet; - } - internal static RealmDictionary DictionaryCreatePopulate(Realm realm, DictionaryHandle handle, RealmValue content) { var newDict = new RealmDictionary(realm, handle, null); diff --git a/Realm/Realm/Helpers/Operator.cs b/Realm/Realm/Helpers/Operator.cs index 3059ab8cba..5f84e63884 100644 --- a/Realm/Realm/Helpers/Operator.cs +++ b/Realm/Realm/Helpers/Operator.cs @@ -364,8 +364,6 @@ internal static class Operator [(typeof(IRealmObjectBase), typeof(RealmValue))] = new IRealmObjectBaseRealmValueConverter(), [(typeof(IList), typeof(RealmValue))] = new IListRealmValueConverter(), [(typeof(RealmValue), typeof(IList))] = new RealmValueIListConverter(), - [(typeof(ISet), typeof(RealmValue))] = new ISetRealmValueConverter(), - [(typeof(RealmValue), typeof(ISet))] = new RealmValueISetConverter(), [(typeof(IDictionary), typeof(RealmValue))] = new IDictionaryRealmValueConverter(), [(typeof(RealmValue), typeof(IDictionary))] = new RealmValueIDictionaryConverter(), }; @@ -813,11 +811,6 @@ private class IListRealmValueConverter : SpecializedConverterBase? value) => value is null ? RealmValue.Null : RealmValue.List(value); } - private class ISetRealmValueConverter : SpecializedConverterBase, RealmValue> - { - public override RealmValue Convert(ISet? value) => value is null ? RealmValue.Null : RealmValue.Set(value); - } - private class IDictionaryRealmValueConverter : SpecializedConverterBase, RealmValue> { public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); @@ -1021,11 +1014,6 @@ private class RealmValueIListConverter : SpecializedConverterBase Convert(RealmValue value) => value.AsList(); } - private class RealmValueISetConverter : SpecializedConverterBase> - { - public override ISet Convert(RealmValue value) => value.AsSet(); - } - private class RealmValueIDictionaryConverter : SpecializedConverterBase> { public override IDictionary Convert(RealmValue value) => value.AsDictionary(); diff --git a/Realm/Realm/Helpers/Operator.tt b/Realm/Realm/Helpers/Operator.tt index 55df0e8ccc..969aef067d 100644 --- a/Realm/Realm/Helpers/Operator.tt +++ b/Realm/Realm/Helpers/Operator.tt @@ -77,8 +77,6 @@ namespace Realms.Helpers [(typeof(IRealmObjectBase), typeof(RealmValue))] = new IRealmObjectBaseRealmValueConverter(), [(typeof(IList), typeof(RealmValue))] = new IListRealmValueConverter(), [(typeof(RealmValue), typeof(IList))] = new RealmValueIListConverter(), - [(typeof(ISet), typeof(RealmValue))] = new ISetRealmValueConverter(), - [(typeof(RealmValue), typeof(ISet))] = new RealmValueISetConverter(), [(typeof(IDictionary), typeof(RealmValue))] = new IDictionaryRealmValueConverter(), [(typeof(RealmValue), typeof(IDictionary))] = new RealmValueIDictionaryConverter(), }; @@ -353,11 +351,6 @@ namespace Realms.Helpers public override RealmValue Convert(IList? value) => value is null ? RealmValue.Null : RealmValue.List(value); } - private class ISetRealmValueConverter : SpecializedConverterBase, RealmValue> - { - public override RealmValue Convert(ISet? value) => value is null ? RealmValue.Null : RealmValue.Set(value); - } - private class IDictionaryRealmValueConverter : SpecializedConverterBase, RealmValue> { public override RealmValue Convert(IDictionary? value) => value is null ? RealmValue.Null : RealmValue.Dictionary(value); @@ -388,11 +381,6 @@ namespace Realms.Helpers public override IList Convert(RealmValue value) => value.AsList(); } - private class RealmValueISetConverter : SpecializedConverterBase> - { - public override ISet Convert(RealmValue value) => value.AsSet(); - } - private class RealmValueIDictionaryConverter : SpecializedConverterBase> { public override IDictionary Convert(RealmValue value) => value.AsDictionary(); @@ -439,7 +427,6 @@ namespace Realms.Helpers private static readonly string[] _implicitTypeExcludeList = new[] { "List?", - "HashSet?", "Dictionary> ListGenerator = i => { return new List { $"inner{i}", i }; }; - public static Func> SetGenerator = i => { return new HashSet { $"inner{i}", i }; }; public static Func> DictGenerator = i => { return new Dictionary { { "s1", i }, { "s2", $"ah{i}" } }; }; public static Func[] CollectionGenerators = new Func[] { i => (RealmValue)ListGenerator(i), - i => (RealmValue)SetGenerator(i), i => (RealmValue)DictGenerator(i), }; @@ -54,7 +52,6 @@ private RealmValueObject PersistAndFind(RealmValue rv) public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); var innerDict = DictGenerator(1); var originalList = new List @@ -72,7 +69,6 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), new InternalObject { IntProperty = 10, StringProperty = "brown" }, innerList, - innerSet, innerDict, }; @@ -98,13 +94,11 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa public void List_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) { var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); var innerDict = DictGenerator(1); var originalList = new List { innerList, - innerSet, innerDict, }; @@ -116,11 +110,9 @@ public void List_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true } var retrievedList = rv.AsList()[0]; - var retrivedSet = rv.AsList()[1]; - var retrievedDict = rv.AsList()[2]; + var retrievedDict = rv.AsList()[1]; Assert.That(retrievedList.AsList(), Is.EqualTo(innerList)); - Assert.That(retrivedSet.AsSet(), Is.EquivalentTo(innerSet)); Assert.That(retrievedDict.AsDictionary(), Is.EquivalentTo(innerDict)); } @@ -334,7 +326,6 @@ public void List_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func { "inner2", true, 2.0 }; - var innerSet = new HashSet { 1, "str", true }; var innerDict = new Dictionary { { "s1", 1 }, @@ -342,7 +333,7 @@ public void List_RemoveWithCollectionArgument_ReturnsFalse() { "s3", true }, }; - var listVal = new List { innerList, innerSet, innerDict }; + var listVal = new List { innerList, innerDict }; var rvo = _realm.Write(() => { @@ -352,7 +343,6 @@ public void List_RemoveWithCollectionArgument_ReturnsFalse() _realm.Write(() => { Assert.That(rvo.RealmValueProperty.AsList().Remove(innerList), Is.False); - Assert.That(rvo.RealmValueProperty.AsList().Remove(innerSet), Is.False); Assert.That(rvo.RealmValueProperty.AsList().Remove(innerDict), Is.False); }); } @@ -430,282 +420,12 @@ public void List_WhenManaged_WorksWithNotifications() #endregion - #region Set - - [Test] - public void Set_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) - { - var originalSet = new HashSet - { - RealmValue.Null, - 1, - true, - "string", - new byte[] { 0, 1, 2 }, - new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), - 1.5f, - 2d, - 3m, - new ObjectId("5f63e882536de46d71877979"), - Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), - new InternalObject { IntProperty = 10, StringProperty = "brown" }, - }; - - RealmValue rv = originalSet; - - if (isManaged) - { - rv = PersistAndFind(rv).RealmValueProperty; - } - - Assert.That(rv.Type, Is.EqualTo(RealmValueType.Set)); - Assert.That(rv != RealmValue.Null); - - Assert.That(rv.AsSet(), Is.EquivalentTo(originalSet)); - Assert.That(rv.AsAny(), Is.EquivalentTo(originalSet)); - Assert.That(rv.As>(), Is.EquivalentTo(originalSet)); - - Assert.That(rv == originalSet); - Assert.That(rv.Equals(originalSet)); - } - - [Test] - public void Set_CanBeCopiedFromManagedSet([Values(true, false)] bool isManaged) - { - var originalSet = SetGenerator(1); - - RealmValue rv = originalSet; - - if (isManaged) - { - rv = PersistAndFind(rv).RealmValueProperty; - } - - var newObj = new RealmValueObject { RealmValueProperty = rv }; - - RealmValue rv2; - - if (isManaged) - { - rv2 = PersistAndFind(rv).RealmValueProperty; - } - else - { - rv2 = newObj.RealmValueProperty; - } - - Assert.That(rv.AsSet(), Is.EquivalentTo(rv2.AsSet())); - Assert.That(rv.AsSet(), Is.EquivalentTo(originalSet)); - Assert.That(rv2.AsSet(), Is.EquivalentTo(originalSet)); - } - - [Test] - public void Set_WhenBeingAddedToRealmWithInternalCollections_Throws() - { - var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); - var innerDict = DictGenerator(1); - - var errorMessage = "Set cannot contain other collections"; - - RealmValue rv = new HashSet() { 1, "string", true, innerList }; - Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), - Throws.TypeOf().And.Message.Contains(errorMessage)); - - rv = new HashSet() { 1, "string", true, innerSet }; - Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), - Throws.TypeOf().And.Message.Contains(errorMessage)); - - rv = new HashSet() { 1, "string", true, innerDict }; - Assert.That(() => _realm.Write(() => _realm.Add(new RealmValueObject { RealmValueProperty = rv })), - Throws.TypeOf().And.Message.Contains(errorMessage)); - } - - [Test] - public void Set_WhenAddingCollections_Throws() - { - var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); - var innerDict = DictGenerator(1); - - var errorMessage = "Set cannot contain other collections"; - - var originalSet = new HashSet() { 1, "string", true }; - - var rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject { RealmValueProperty = originalSet }); - }); - - Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerList)), - Throws.TypeOf().And.Message.Contains(errorMessage)); - - Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerSet)), - Throws.TypeOf().And.Message.Contains(errorMessage)); - - Assert.That(() => _realm.Write(() => rvo.RealmValueProperty.AsSet().Add(innerDict)), - Throws.TypeOf().And.Message.Contains(errorMessage)); - } - - [Test] - public void Set_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) - { - var originalSet = SetGenerator(1); - - RealmValue rvOperator = originalSet; - RealmValue rvConstructor = RealmValue.Set(originalSet); - RealmValue rvCreate = RealmValue.Create(originalSet, RealmValueType.Set); - - if (isManaged) - { - rvOperator = PersistAndFind(rvOperator).RealmValueProperty; - rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; - rvCreate = PersistAndFind(rvCreate).RealmValueProperty; - } - - Assert.That(rvOperator.AsSet(), Is.EquivalentTo(originalSet)); - Assert.That(rvConstructor.AsSet(), Is.EquivalentTo(originalSet)); - Assert.That(rvCreate.AsSet(), Is.EquivalentTo(originalSet)); - } - - [Test] - public void Set_WhenManaged_IsNotSameReferenceAsOriginalList() - { - var originalSet = SetGenerator(1); - - RealmValue rv = originalSet; - rv = PersistAndFind(rv).RealmValueProperty; - var retrievedSet = rv.AsSet(); - - Assert.That(ReferenceEquals(originalSet, retrievedSet), Is.False); - } - - [Test] - public void Set_WhenUnmanaged_IsSameReferenceAsOriginalList() - { - var originalSet = SetGenerator(1); - - RealmValue rv = originalSet; - var retrievedSet = rv.AsSet(); - - Assert.That(ReferenceEquals(originalSet, retrievedSet), Is.True); - } - - [Test] - public void Set_AfterCreation_CanBeAssigned([Values(true, false)] bool isManaged) - { - var stringVal = "Mario"; - var rvo = new RealmValueObject { RealmValueProperty = stringVal }; - - if (isManaged) - { - _realm.Write(() => - { - _realm.Add(rvo); - }); - } - - Assert.That(rvo.RealmValueProperty == stringVal); - - var setVal = SetGenerator(1); - - _realm.Write(() => - { - rvo.RealmValueProperty = setVal; - }); - - Assert.That(rvo.RealmValueProperty.AsSet(), Is.EquivalentTo(setVal)); - - var newStringVal = "Luigi"; - - _realm.Write(() => - { - rvo.RealmValueProperty = newStringVal; - }); - - Assert.That(rvo.RealmValueProperty == newStringVal); - } - - [Test] - public void Set_WhenManaged_WorksWithDynamicLikeApi() - { - var originalSet = SetGenerator(1); - - var rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject()); - }); - - _realm.Write(() => - { - rvo.DynamicApi.Set(nameof(RealmValueObject.RealmValueProperty), originalSet); - }); - - var rvp = rvo.DynamicApi.Get(nameof(RealmValueObject.RealmValueProperty)); - - Assert.That(rvp.AsSet(), Is.EquivalentTo(originalSet)); - } - -#if !UNITY - [Test] -#endif - public void Set_WhenManaged_WorksWithDynamic() - { - var originalSet = SetGenerator(1); - - dynamic rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject()); - }); - - _realm.Write(() => - { - rvo.RealmValueProperty = originalSet; - }); - - var rvp = rvo.RealmValueProperty; - - Assert.That(rvp.AsSet(), Is.EquivalentTo(originalSet)); - } - - [Test] - public void Set_WhenManaged_WorksWithNotifications() - { - var originalSet = SetGenerator(1); - - var rvo = _realm.Write(() => - { - return _realm.Add(new RealmValueObject { RealmValueProperty = originalSet }); - }); - - var callbacks = new List(); - using var token = rvo.RealmValueProperty.AsSet().SubscribeForNotifications((collection, changes) => - { - if (changes != null) - { - callbacks.Add(changes); - } - }); - - _realm.Write(() => - { - rvo.RealmValueProperty.AsSet().Add("mario"); - }); - - _realm.Refresh(); - - Assert.That(callbacks.Count, Is.EqualTo(1)); - } - - #endregion - #region Dictionary [Test] public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isManaged) { var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); var innerDict = DictGenerator(1); var originalDict = new Dictionary @@ -724,7 +444,6 @@ public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] boo { "c", new InternalObject { IntProperty = 10, StringProperty = "brown" } }, { "d", innerList }, { "e", innerDict }, - { "f", innerSet }, }; RealmValue rv = originalDict; @@ -749,14 +468,12 @@ public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] boo public void Dictionary_WhenUsingIndexAccessWithCollections_WorkAsExpected([Values(true, false)] bool isManaged) { var innerList = ListGenerator(1); - var innerSet = SetGenerator(1); var innerDict = DictGenerator(1); var originalDict = new Dictionary { { "0", innerList }, - { "1", innerSet }, - { "2", innerDict }, + { "1", innerDict }, }; RealmValue rv = originalDict; @@ -767,19 +484,15 @@ public void Dictionary_WhenUsingIndexAccessWithCollections_WorkAsExpected([Value } var retrievedList = rv.AsDictionary().ElementAt(0).Value; - var retrivedSet = rv.AsDictionary().ElementAt(1).Value; - var retrievedDict = rv.AsDictionary().ElementAt(2).Value; + var retrievedDict = rv.AsDictionary().ElementAt(1).Value; Assert.That(retrievedList.AsList(), Is.EqualTo(innerList)); - Assert.That(retrivedSet.AsSet(), Is.EquivalentTo(innerSet)); Assert.That(retrievedDict.AsDictionary(), Is.EquivalentTo(innerDict)); var retrievedList2 = rv.AsDictionary()["0"]; - var retrivedSet2 = rv.AsDictionary()["1"]; - var retrievedDict2 = rv.AsDictionary()["2"]; + var retrievedDict2 = rv.AsDictionary()["1"]; Assert.That(retrievedList2.AsList(), Is.EqualTo(innerList)); - Assert.That(retrivedSet2.AsSet(), Is.EquivalentTo(innerSet)); Assert.That(retrievedDict2.AsDictionary(), Is.EquivalentTo(innerDict)); } @@ -813,6 +526,8 @@ public void Dictionary_CanBeCopiedFromManagedDictionary([Values(true, false)] bo Assert.That(rv2.AsDictionary(), Is.EqualTo(originalDict)); } + //TODO Add test to verify sets can't be in RealmValue + [Test] public void Dictionary_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { @@ -973,13 +688,11 @@ public void Dictionary_AddSetInsertMoveRemoveCollection_WorksAsIntended(Func { { "s1", innerList }, - { "s2", innerSet }, { "s3", innerDict }, }; @@ -991,7 +704,6 @@ public void Dictionary_RemoveWithCollectionArgument_ReturnsFalse() _realm.Write(() => { Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s1", innerList)), Is.False); - Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s2", innerSet)), Is.False); Assert.That(rvo.RealmValueProperty.AsDictionary().Remove(new KeyValuePair("s3", innerDict)), Is.False); }); } From 2498be7478b91365cc411a7448502625025861f4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 13 Nov 2023 16:35:44 +0100 Subject: [PATCH 57/88] Simplified CreateInternalCollectionAndPopulate --- .../DatabaseTypes/RealmCollectionBase.cs | 18 +++++++++++++ Realm/Realm/DatabaseTypes/RealmDictionary.cs | 24 ++--------------- Realm/Realm/DatabaseTypes/RealmList.cs | 26 +++---------------- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs index e8c1c09666..88d0721b8c 100644 --- a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs +++ b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs @@ -262,6 +262,24 @@ private void UnsubscribeFromNotifications(NotificationCallbackDelegate callba _notificationCallbacks.Value.Remove(callback, shallow); } + protected void CreateInternalCollectionAndPopulate(RealmValue realmValue, IntPtr collectionPtr) + { + switch (realmValue.Type) + { + case RealmValueType.List: + var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); + break; + case RealmValueType.Dictionary: + var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); + CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); + break; + default: + Debug.Fail("Invalid collection type"); + break; + } + } + #region INotifyCollectionChanged private void OnChange(IRealmCollection sender, ChangeSet? change) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index d4115f10d3..7d68fffc54 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -67,7 +67,7 @@ public TValue this[string key] if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, () => _dictionaryHandle.SetCollection(key, realmValue.Type)); + CreateInternalCollectionAndPopulate(realmValue, _dictionaryHandle.SetCollection(key, realmValue.Type)); return; } @@ -132,7 +132,7 @@ public void Add(string key, TValue value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, () => _dictionaryHandle.AddCollection(key, realmValue.Type)); + CreateInternalCollectionAndPopulate(realmValue, _dictionaryHandle.AddCollection(key, realmValue.Type)); return; } @@ -242,26 +242,6 @@ private void UnsubscribeFromNotifications() _deliveredInitialKeyNotification = false; } - private void CreateInternalCollectionAndPopulate(RealmValue realmValue, Func createCollectionFunc) - { - var collectionPtr = createCollectionFunc(); - - switch (realmValue.Type) - { - case RealmValueType.List: - var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); - break; - case RealmValueType.Dictionary: - var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - } - private static void ValidateKey(string key) { if (key == null) diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 1530345361..5a9f23594b 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -69,7 +69,7 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.SetCollection(index, realmValue.Type)); + CreateInternalCollectionAndPopulate(realmValue, _listHandle.SetCollection(index, realmValue.Type)); return; } @@ -97,7 +97,7 @@ public void Add(T value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.AddCollection(realmValue.Type)); + CreateInternalCollectionAndPopulate(realmValue, _listHandle.AddCollection(realmValue.Type)); return; } @@ -140,7 +140,7 @@ public void Insert(int index, T value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, () => _listHandle.InsertCollection(index, realmValue.Type)); + CreateInternalCollectionAndPopulate(realmValue, _listHandle.InsertCollection(index, realmValue.Type)); return; } @@ -200,26 +200,6 @@ internal RealmResults ToResults() DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression expression) => new MetaRealmList(expression, this); - private void CreateInternalCollectionAndPopulate(RealmValue realmValue, Func createCollectionFunc) - { - var collectionPtr = createCollectionFunc(); - - switch (realmValue.Type) - { - case RealmValueType.List: - var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); - break; - case RealmValueType.Dictionary: - var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - } - private static void ValidateIndex(int index, string name = "index") { if (index < 0) From ff99118f66278a9f37c7ad3f4be7effc01246637 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:28:12 +0100 Subject: [PATCH 58/88] Ulterior removal of set and added method parameter name --- Realm/Realm/Helpers/CollectionHelpers.cs | 4 ++-- wrappers/src/dictionary_cs.cpp | 6 ------ wrappers/src/list_cs.cpp | 9 --------- wrappers/src/marshalling.hpp | 15 --------------- wrappers/src/object_cs.cpp | 3 --- 5 files changed, 2 insertions(+), 35 deletions(-) diff --git a/Realm/Realm/Helpers/CollectionHelpers.cs b/Realm/Realm/Helpers/CollectionHelpers.cs index 868c1d95e7..fde147a7ee 100644 --- a/Realm/Realm/Helpers/CollectionHelpers.cs +++ b/Realm/Realm/Helpers/CollectionHelpers.cs @@ -22,7 +22,7 @@ internal static class CollectionHelpers { internal static RealmList ListCreateAndPopulate(Realm realm, ListHandle handle, RealmValue content) { - var newList = new RealmList(realm, handle, null); + var newList = new RealmList(realm, handle, metadata: null); foreach (var item in content.AsList()) { @@ -34,7 +34,7 @@ internal static RealmList ListCreateAndPopulate(Realm realm, ListHan internal static RealmDictionary DictionaryCreatePopulate(Realm realm, DictionaryHandle handle, RealmValue content) { - var newDict = new RealmDictionary(realm, handle, null); + var newDict = new RealmDictionary(realm, handle, metadata: null); foreach (var item in content.AsDictionary()) { diff --git a/wrappers/src/dictionary_cs.cpp b/wrappers/src/dictionary_cs.cpp index 7a0a1215fd..064dd9150b 100644 --- a/wrappers/src/dictionary_cs.cpp +++ b/wrappers/src/dictionary_cs.cpp @@ -86,9 +86,6 @@ extern "C" { case realm::binding::realm_value_type::RLM_TYPE_LIST: dictionary.insert_collection(dict_key, CollectionType::List); return new List(dictionary.get_list(dict_key)); - case realm::binding::realm_value_type::RLM_TYPE_SET: - dictionary.insert_collection(dict_key, CollectionType::Set); - return new object_store::Set(dictionary.get_set(dict_key)); case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: dictionary.insert_collection(dict_key, CollectionType::Dictionary); return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); @@ -125,9 +122,6 @@ extern "C" { case realm::binding::realm_value_type::RLM_TYPE_LIST: dictionary.insert_collection(dict_key, CollectionType::List); return new List(dictionary.get_list(dict_key)); - case realm::binding::realm_value_type::RLM_TYPE_SET: - dictionary.insert_collection(dict_key, CollectionType::Set); - return new object_store::Set(dictionary.get_set(dict_key)); case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: dictionary.insert_collection(dict_key, CollectionType::Dictionary); return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); diff --git a/wrappers/src/list_cs.cpp b/wrappers/src/list_cs.cpp index f5dd4e37ca..6965d2f53e 100644 --- a/wrappers/src/list_cs.cpp +++ b/wrappers/src/list_cs.cpp @@ -96,9 +96,6 @@ REALM_EXPORT void* list_set_collection(List& list, size_t list_ndx, realm_value_ case realm::binding::realm_value_type::RLM_TYPE_LIST: list.set_collection(list_ndx, CollectionType::List); return new List(list.get_list(list_ndx)); - case realm::binding::realm_value_type::RLM_TYPE_SET: - list.set_collection(list_ndx, CollectionType::Set); - return new object_store::Set(list.get_set(list_ndx)); case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: list.set_collection(list_ndx, CollectionType::Dictionary); return new object_store::Dictionary(list.get_dictionary(list_ndx)); @@ -157,9 +154,6 @@ REALM_EXPORT void* list_insert_collection(List& list, size_t list_ndx, realm_val case realm::binding::realm_value_type::RLM_TYPE_LIST: list.insert_collection(list_ndx, CollectionType::List); return new List(list.get_list(list_ndx)); - case realm::binding::realm_value_type::RLM_TYPE_SET: - list.insert_collection(list_ndx, CollectionType::Set); - return new object_store::Set(list.get_set(list_ndx)); case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: list.insert_collection(list_ndx, CollectionType::Dictionary); return new object_store::Dictionary(list.get_dictionary(list_ndx)); @@ -212,9 +206,6 @@ REALM_EXPORT void list_get_value(List& list, size_t ndx, realm_value_t* value, N case type_List: *value = to_capi(new List(list.get_list(ndx))); break; - case type_Set: - *value = to_capi(new object_store::Set(list.get_set(ndx))); - break; case type_Dictionary: *value = to_capi(new object_store::Dictionary(list.get_dictionary(ndx))); break; diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 0c9a52b1a8..f6e3de1798 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -102,7 +102,6 @@ enum class realm_value_type : uint8_t { RLM_TYPE_LINK, RLM_TYPE_UUID, RLM_TYPE_LIST, - RLM_TYPE_SET, RLM_TYPE_DICTIONARY, }; @@ -141,10 +140,6 @@ typedef struct realm_list { List* list_ptr; } realm_list_t; -typedef struct realm_set { - object_store::Set* set_ptr; -} realm_set_t; - typedef struct realm_dict { object_store::Dictionary* dictionary_ptr; } realm_dict_t; @@ -172,7 +167,6 @@ typedef struct realm_value { realm_link_t link; realm_list_t list; - realm_set_t set; realm_dict_t dictionary; char data[16]; @@ -527,14 +521,6 @@ static inline realm_value_t to_capi(List* list) return val; } -static inline realm_value_t to_capi(object_store::Set* set) -{ - realm_value_t val{}; - val.type = realm_value_type::RLM_TYPE_SET; - val.set.set_ptr = set; - return val; -} - static inline realm_value_t to_capi(object_store::Dictionary* dictionary) { realm_value_t val{}; @@ -606,7 +592,6 @@ static inline realm_value_t to_capi(const Mixed& value) break; } case type_List: - case type_Set: case type_Dictionary: REALM_TERMINATE("Can't use this overload of to_capi on values containing collections, use to_capi(Collection*) instead."); default: diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 7469825cb3..68a979cfde 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -118,9 +118,6 @@ extern "C" { case type_List: *value = to_capi(new List(object.realm(), object.get_obj(), prop.column_key)); break; - case type_Set: - *value = to_capi(new object_store::Set(object.realm(), object.get_obj(), prop.column_key)); - break; case type_Dictionary: *value = to_capi(new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key)); break; From 7f0456ff2a8165831d8d0e22d58f1d2ced9dc3bc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 13:59:12 +0100 Subject: [PATCH 59/88] Removed other set-related things, added implicit method from array of RealmValue --- Realm/Realm/DatabaseTypes/RealmValue.cs | 7 +++++++ Realm/Realm/Native/PrimitiveValue.cs | 15 --------------- Tests/Realm.Tests/Database/RealmSetTests.cs | 12 ++++++------ .../Database/RealmValueWithCollections.cs | 5 ++++- wrappers/src/marshalling.hpp | 1 + wrappers/src/object_cs.cpp | 3 --- 6 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index 11a17f5b7d..ed24658b59 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -1448,6 +1448,13 @@ public override int GetHashCode() /// A containing the supplied . public static implicit operator RealmValue(List? val) => val == null ? Null : List(val); + /// + /// Implicitly constructs a from RealmValue[]?. + /// + /// The value to store in the . + /// A containing the supplied . + public static implicit operator RealmValue(RealmValue[]? val) => val == null ? Null : List(val); + /// /// Implicitly constructs a from Dictionary<string, RealmValue>. /// diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index 439e85db07..4f2e607f2e 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -71,9 +71,6 @@ internal unsafe struct PrimitiveValue [FieldOffset(0)] private ListValue list_value; - [FieldOffset(0)] - private SetValue set_value; - [FieldOffset(0)] private DictionaryValue dictionary_value; @@ -279,12 +276,6 @@ public readonly RealmList AsList(Realm realm) return new RealmList(realm, handle, null); } - public readonly RealmSet AsSet(Realm realm) - { - var handle = new SetHandle(realm.SharedRealmHandle, set_value.set_ptr); - return new RealmSet(realm, handle, null); - } - public readonly RealmDictionary AsDictionary(Realm realm) { var handle = new DictionaryHandle(realm.SharedRealmHandle, dictionary_value.dict_ptr); @@ -318,12 +309,6 @@ private struct ListValue public IntPtr list_ptr; } - [StructLayout(LayoutKind.Sequential)] - private struct SetValue - { - public IntPtr set_ptr; - } - [StructLayout(LayoutKind.Sequential)] private struct DictionaryValue { diff --git a/Tests/Realm.Tests/Database/RealmSetTests.cs b/Tests/Realm.Tests/Database/RealmSetTests.cs index 7b81e291e8..bdc27b3d87 100644 --- a/Tests/Realm.Tests/Database/RealmSetTests.cs +++ b/Tests/Realm.Tests/Database/RealmSetTests.cs @@ -1092,15 +1092,15 @@ private static IEnumerable> RealmTestValues() var rv10 = RealmValue.Create(new Guid("{F2952191-A847-41C3-8362-497F92CB7D24}"), RealmValueType.Guid); var rv11 = GetRealmValueObject(); - yield return new TestCaseData(new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 }, new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 }); + yield return new TestCaseData(new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 } as IEnumerable, new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 }); rv11 = GetRealmValueObject(); - yield return new TestCaseData(new[] { rv0, rv1, rv2, rv3, rv4, rv5 }, new[] { rv6, rv7, rv8, rv9, rv10, rv11 }); + yield return new TestCaseData(new[] { rv0, rv1, rv2, rv3, rv4, rv5 } as IEnumerable, new[] { rv6, rv7, rv8, rv9, rv10, rv11 }); rv11 = GetRealmValueObject(); - yield return new TestCaseData(Array.Empty(), new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 }); + yield return new TestCaseData(Array.Empty() as IEnumerable, new[] { rv0, rv1, rv2, rv3, rv4, rv5, rv6, rv7, rv8, rv9, rv10, rv11 }); static RealmValue GetRealmValueObject() => RealmValue.Create(new IntPropertyObject { Int = 10 }, RealmValueType.Object); @@ -1110,21 +1110,21 @@ private static IEnumerable> RealmTestValues() var i4 = RealmValue.Create(true, RealmValueType.Bool); var i5 = RealmValue.Create(1m, RealmValueType.Decimal128); - yield return new TestCaseData(new[] { i1, i2, i3, i4, i5 }, new[] { i1, i2, i3, i4, i5 }); + yield return new TestCaseData(new[] { i1, i2, i3, i4, i5 } as IEnumerable, new[] { i1, i2, i3, i4, i5 }); var s1 = RealmValue.Create(string.Empty, RealmValueType.String); var s2 = RealmValue.Create(0, RealmValueType.Int); var s3 = RealmValue.Create(Guid.Empty, RealmValueType.Guid); var s4 = RealmValue.Null; - yield return new TestCaseData(new[] { s1, s2, s3, s4 }, new[] { s1, s2, s3, s4 }); + yield return new TestCaseData(new[] { s1, s2, s3, s4 } as IEnumerable, new[] { s1, s2, s3, s4 }); var d1 = RealmValue.Create(1m, RealmValueType.Decimal128); var d2 = RealmValue.Create(1f, RealmValueType.Decimal128); var d3 = RealmValue.Create(1d, RealmValueType.Decimal128); var d4 = RealmValue.Create(1, RealmValueType.Decimal128); - yield return new TestCaseData(new[] { d1, d2, d3, d4 }, new[] { d1, d2, d3, d4 }); + yield return new TestCaseData(new[] { d1, d2, d3, d4 } as IEnumerable, new[] { d1, d2, d3, d4 }); } [TestCaseSource(nameof(RealmTestValues))] diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 912af1ffc4..bb46f0c7eb 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -147,24 +147,27 @@ public void List_CanBeCopiedFromManagedList([Values(true, false)] bool isManaged } [Test] - public void List_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) + public void List_BuiltWithConstructorMethodOrOperatorOrCreateOrArray_WorksTheSame([Values(true, false)] bool isManaged) { var originalList = ListGenerator(1); RealmValue rvOperator = originalList; RealmValue rvConstructor = RealmValue.List(originalList); RealmValue rvCreate = RealmValue.Create(originalList, RealmValueType.List); + RealmValue rvArray = originalList.ToArray(); if (isManaged) { rvOperator = PersistAndFind(rvOperator).RealmValueProperty; rvConstructor = PersistAndFind(rvConstructor).RealmValueProperty; rvCreate = PersistAndFind(rvCreate).RealmValueProperty; + rvArray = PersistAndFind(rvCreate).RealmValueProperty; } Assert.That(rvOperator.AsList(), Is.EqualTo(originalList)); Assert.That(rvConstructor.AsList(), Is.EqualTo(originalList)); Assert.That(rvCreate.AsList(), Is.EqualTo(originalList)); + Assert.That(rvArray.AsList(), Is.EqualTo(originalList)); } [Test] diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index f6e3de1798..52d558002e 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -102,6 +102,7 @@ enum class realm_value_type : uint8_t { RLM_TYPE_LINK, RLM_TYPE_UUID, RLM_TYPE_LIST, + RLM_TYPE_SET, RLM_TYPE_DICTIONARY, }; diff --git a/wrappers/src/object_cs.cpp b/wrappers/src/object_cs.cpp index 68a979cfde..89444aeb1d 100644 --- a/wrappers/src/object_cs.cpp +++ b/wrappers/src/object_cs.cpp @@ -186,9 +186,6 @@ extern "C" { case realm::binding::realm_value_type::RLM_TYPE_LIST: object.get_obj().set_collection(prop.column_key, CollectionType::List); return new List(object.realm(), object.get_obj(), prop.column_key); - case realm::binding::realm_value_type::RLM_TYPE_SET: - object.get_obj().set_collection(prop.column_key, CollectionType::Set); - return new object_store::Set(object.realm(), object.get_obj(), prop.column_key); case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: object.get_obj().set_collection(prop.column_key, CollectionType::Dictionary); return new object_store::Dictionary(object.realm(), object.get_obj(), prop.column_key); From 77c439e79492ade365edd2baef72c0b50c22e42e Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:04:17 +0100 Subject: [PATCH 60/88] Changed name to method --- Realm/Realm/DatabaseTypes/RealmCollectionBase.cs | 4 ++-- Realm/Realm/Handles/ObjectHandle.cs | 4 ++-- Realm/Realm/Helpers/CollectionHelpers.cs | 8 ++------ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs index 88d0721b8c..a0746f7e49 100644 --- a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs +++ b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs @@ -268,11 +268,11 @@ protected void CreateInternalCollectionAndPopulate(RealmValue realmValue, IntPtr { case RealmValueType.List: var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.ListCreateAndPopulate(Realm, listHandle, realmValue); + CollectionHelpers.PopulateList(Realm, listHandle, realmValue); break; case RealmValueType.Dictionary: var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.DictionaryCreatePopulate(Realm, dictionaryHandle, realmValue); + CollectionHelpers.PopulateDictionary(Realm, dictionaryHandle, realmValue); break; default: Debug.Fail("Invalid collection type"); diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index e46cb422e0..36a19ed6c7 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -231,10 +231,10 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value switch (value.Type) { case RealmValueType.List: - CollectionHelpers.ListCreateAndPopulate(realm, new ListHandle(Root!, collectionPtr), value); + CollectionHelpers.PopulateList(realm, new ListHandle(Root!, collectionPtr), value); break; case RealmValueType.Dictionary: - CollectionHelpers.DictionaryCreatePopulate(realm, new DictionaryHandle(Root!, collectionPtr), value); + CollectionHelpers.PopulateDictionary(realm, new DictionaryHandle(Root!, collectionPtr), value); break; default: break; diff --git a/Realm/Realm/Helpers/CollectionHelpers.cs b/Realm/Realm/Helpers/CollectionHelpers.cs index fde147a7ee..bb50d9a842 100644 --- a/Realm/Realm/Helpers/CollectionHelpers.cs +++ b/Realm/Realm/Helpers/CollectionHelpers.cs @@ -20,7 +20,7 @@ namespace Realms.Helpers; internal static class CollectionHelpers { - internal static RealmList ListCreateAndPopulate(Realm realm, ListHandle handle, RealmValue content) + internal static void PopulateList(Realm realm, ListHandle handle, RealmValue content) { var newList = new RealmList(realm, handle, metadata: null); @@ -28,11 +28,9 @@ internal static RealmList ListCreateAndPopulate(Realm realm, ListHan { newList.Add(item); } - - return newList; } - internal static RealmDictionary DictionaryCreatePopulate(Realm realm, DictionaryHandle handle, RealmValue content) + internal static void PopulateDictionary(Realm realm, DictionaryHandle handle, RealmValue content) { var newDict = new RealmDictionary(realm, handle, metadata: null); @@ -40,7 +38,5 @@ internal static RealmDictionary DictionaryCreatePopulate(Realm realm { newDict.Add(item); } - - return newDict; } } From aa1a3153d47532dd22e24ec5faaa658ea14deffd Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:52:42 +0100 Subject: [PATCH 61/88] Added explicit sequence equals --- Tests/Realm.Tests/Database/RealmValueWithCollections.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index bb46f0c7eb..e1b08eafef 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -88,6 +88,7 @@ public void List_WhenRetrieved_WorksWithAllTypes([Values(true, false)] bool isMa Assert.That(rv == originalList); Assert.That(rv.Equals(originalList)); + Assert.That(rv.AsList().SequenceEqual(originalList)); } [Test] @@ -463,7 +464,7 @@ public void Dictionary_WhenRetrieved_WorksWithAllTypes([Values(true, false)] boo Assert.That(rv.AsAny(), Is.EquivalentTo(originalDict)); Assert.That(rv.As>(), Is.EquivalentTo(originalDict)); - Assert.That(rv == originalDict, "Equal to than original dict"); + Assert.That(rv == originalDict, "Equal to original dict"); Assert.That(rv.Equals(originalDict), "Equal to original dict with Equals"); } @@ -529,8 +530,6 @@ public void Dictionary_CanBeCopiedFromManagedDictionary([Values(true, false)] bo Assert.That(rv2.AsDictionary(), Is.EqualTo(originalDict)); } - //TODO Add test to verify sets can't be in RealmValue - [Test] public void Dictionary_BuiltWithConstructorMethodOrOperatorOrCreate_WorksTheSame([Values(true, false)] bool isManaged) { From 041e0cf9b414114785fb33f98310d082761b0812 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:32:44 +0100 Subject: [PATCH 62/88] Unified set_collection and add_collection for dictionary --- Realm/Realm/Handles/DictionaryHandle.cs | 9 +++------ wrappers/src/dictionary_cs.cpp | 24 ++---------------------- wrappers/src/marshalling.hpp | 6 +++--- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/Realm/Realm/Handles/DictionaryHandle.cs b/Realm/Realm/Handles/DictionaryHandle.cs index 0da75f0e2d..66974f8963 100644 --- a/Realm/Realm/Handles/DictionaryHandle.cs +++ b/Realm/Realm/Handles/DictionaryHandle.cs @@ -76,9 +76,6 @@ private static class NativeMethods [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set_embedded", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr set_embedded(DictionaryHandle handle, PrimitiveValue key, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_set_collection", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr set_collection(DictionaryHandle handle, PrimitiveValue key, RealmValueType type, out NativeException ex); - [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_add", CallingConvention = CallingConvention.Cdecl)] public static extern void add_value(DictionaryHandle handle, PrimitiveValue key, PrimitiveValue value, out NativeException ex); @@ -86,7 +83,7 @@ private static class NativeMethods public static extern IntPtr add_embedded(DictionaryHandle handle, PrimitiveValue key, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_add_collection", CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr add_collection(DictionaryHandle handle, PrimitiveValue key, RealmValueType type, out NativeException ex); + public static extern IntPtr add_collection(DictionaryHandle handle, PrimitiveValue key, RealmValueType type, bool allowOverride, out NativeException ex); [DllImport(InteropConfig.DLL_NAME, EntryPoint = "realm_dictionary_contains_key", CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] @@ -257,7 +254,7 @@ public IntPtr SetCollection(string key, RealmValueType collectionType) RealmValue keyValue = key; var (primitiveKey, keyHandles) = keyValue.ToNative(); - var result = NativeMethods.set_collection(this, primitiveKey, collectionType, out var nativeException); + var result = NativeMethods.add_collection(this, primitiveKey, collectionType, allowOverride: true, out var nativeException); keyHandles?.Dispose(); nativeException.ThrowIfNecessary(); @@ -300,7 +297,7 @@ public IntPtr AddCollection(string key, RealmValueType collectionType) RealmValue keyValue = key; var (primitiveKey, keyHandles) = keyValue.ToNative(); - var result = NativeMethods.add_collection(this, primitiveKey, collectionType, out var nativeException); + var result = NativeMethods.add_collection(this, primitiveKey, collectionType, allowOverride: false, out var nativeException); keyHandles?.Dispose(); nativeException.ThrowIfNecessary(); diff --git a/wrappers/src/dictionary_cs.cpp b/wrappers/src/dictionary_cs.cpp index 064dd9150b..cad543d303 100644 --- a/wrappers/src/dictionary_cs.cpp +++ b/wrappers/src/dictionary_cs.cpp @@ -71,12 +71,12 @@ extern "C" { }); } - REALM_EXPORT void* realm_dictionary_add_collection(object_store::Dictionary& dictionary, realm_value_t key, realm_value_type type, NativeException::Marshallable& ex) + REALM_EXPORT void* realm_dictionary_add_collection(object_store::Dictionary& dictionary, realm_value_t key, realm_value_type type, bool allow_override, NativeException::Marshallable& ex) { return handle_errors(ex, [&]()-> void* { auto dict_key = from_capi(key.string); - if (dictionary.contains(dict_key)) + if (!allow_override && dictionary.contains(dict_key)) { throw KeyAlreadyExistsException(dict_key); } @@ -111,26 +111,6 @@ extern "C" { }); } - REALM_EXPORT void* realm_dictionary_set_collection(object_store::Dictionary& dictionary, realm_value_t key, realm_value_type type, NativeException::Marshallable& ex) - { - return handle_errors(ex, [&]()-> void* { - - auto dict_key = from_capi(key.string); - - switch (type) - { - case realm::binding::realm_value_type::RLM_TYPE_LIST: - dictionary.insert_collection(dict_key, CollectionType::List); - return new List(dictionary.get_list(dict_key)); - case realm::binding::realm_value_type::RLM_TYPE_DICTIONARY: - dictionary.insert_collection(dict_key, CollectionType::Dictionary); - return new object_store::Dictionary(dictionary.get_dictionary(dict_key)); - default: - REALM_TERMINATE("Invalid collection type"); - } - }); - } - REALM_EXPORT bool realm_dictionary_try_get(object_store::Dictionary& dictionary, realm_value_t key, realm_value_t* value, NativeException::Marshallable& ex) { return handle_errors(ex, [&]() { diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 52d558002e..d3133eaa12 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -514,6 +514,9 @@ static inline realm_value_t to_capi(ObjLink obj_link, SharedRealm realm) return to_capi(realm->read_group().get_object(obj_link), realm); } +// Collections need to have their own overload of to_capi, as at the moment there's no API to retrieve a collection value +// from Mixed (like val.get), so we need first to retrieve the collection itself with the specific methods like +// list.get_list, list.get_dictionary and so on static inline realm_value_t to_capi(List* list) { realm_value_t val{}; @@ -620,9 +623,6 @@ inline realm_value_t to_capi(const object_store::Dictionary& dictionary, const M case type_List: return to_capi(new List(dictionary.get_list(key))); break; - case type_Set: - return to_capi(new object_store::Set(dictionary.get_set(key))); - break; case type_Dictionary: return to_capi(new object_store::Dictionary(dictionary.get_dictionary(key))); break; From c803df9ebdf49ca45722af57e0685acd1633b76d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:49:42 +0100 Subject: [PATCH 63/88] Moved _intValue out of primitiveValue --- Realm/Realm/DatabaseTypes/RealmValue.cs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmValue.cs b/Realm/Realm/DatabaseTypes/RealmValue.cs index ed24658b59..d2e67a1746 100644 --- a/Realm/Realm/DatabaseTypes/RealmValue.cs +++ b/Realm/Realm/DatabaseTypes/RealmValue.cs @@ -62,8 +62,12 @@ namespace Realms [FieldOffset(0)] private readonly PrimitiveValue _primitiveValue; - // This is only used when PrimitiveValue.Type == Int. int_value in PrimitiveValue occupies the first 8 bytes - // so we can safely place this at the 9th byte. + // _intValue has been extracted here to be more memory efficient, as it needs also _propertyIndex + // and _objectHandle when working with RealmInteger. + [FieldOffset(0)] + private readonly long _intValue; + + // This is only used when PrimitiveValue.Type == Int. [FieldOffset(8)] private readonly IntPtr _propertyIndex; @@ -131,10 +135,7 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? _dictionaryValue = primitive.AsDictionary(realm!); break; case RealmValueType.Int: - _primitiveValue = primitive; - - // _propertyIndex and _objectHandler are used only when converting the RealmValue to a RealmInteger. - // _propertyIndex needs to be set after _primitiveValue due to the RealmValue struct explicit layout. + _intValue = primitive.AsInt(); _propertyIndex = propertyIndex; _objectHandle = handle; break; @@ -144,6 +145,12 @@ internal RealmValue(PrimitiveValue primitive, Realm? realm = null, ObjectHandle? } } + private RealmValue(long intValue) : this() + { + _type = RealmValueType.Int; + _intValue = intValue; + } + private RealmValue(byte[] data) : this() { _type = RealmValueType.Data; @@ -182,8 +189,6 @@ private RealmValue(IDictionary dict) : this() private static RealmValue Bool(bool value) => new(PrimitiveValue.Bool(value)); - private static RealmValue Int(long value) => new(PrimitiveValue.Int(value)); - private static RealmValue Float(float value) => new(PrimitiveValue.Float(value)); private static RealmValue Double(double value) => new(PrimitiveValue.Double(value)); @@ -196,6 +201,8 @@ private RealmValue(IDictionary dict) : this() private static RealmValue Guid(Guid value) => new(PrimitiveValue.Guid(value)); + private static RealmValue Int(long value) => new(value); + private static RealmValue Data(byte[] value) => new(value); private static RealmValue String(string value) => new(value); @@ -338,7 +345,7 @@ public int AsInt32() public long AsInt64() { EnsureType("long", RealmValueType.Int); - return _primitiveValue.AsInt(); + return _intValue; } /// From 0d92fefc346817b7abb78c39a0390afc73828058 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:01:23 +0100 Subject: [PATCH 64/88] Removed unnecessary structs --- Realm/Realm/Native/PrimitiveValue.cs | 21 +++------------------ wrappers/src/marshalling.hpp | 7 +++---- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/Realm/Realm/Native/PrimitiveValue.cs b/Realm/Realm/Native/PrimitiveValue.cs index 4f2e607f2e..0f333fcfe9 100644 --- a/Realm/Realm/Native/PrimitiveValue.cs +++ b/Realm/Realm/Native/PrimitiveValue.cs @@ -69,10 +69,7 @@ internal unsafe struct PrimitiveValue private LinkValue link_value; [FieldOffset(0)] - private ListValue list_value; - - [FieldOffset(0)] - private DictionaryValue dictionary_value; + private IntPtr collection_ptr; [FieldOffset(16)] [MarshalAs(UnmanagedType.U1)] @@ -272,13 +269,13 @@ public readonly IRealmObjectBase AsObject(Realm realm) public readonly RealmList AsList(Realm realm) { - var handle = new ListHandle(realm.SharedRealmHandle, list_value.list_ptr); + var handle = new ListHandle(realm.SharedRealmHandle, collection_ptr); return new RealmList(realm, handle, null); } public readonly RealmDictionary AsDictionary(Realm realm) { - var handle = new DictionaryHandle(realm.SharedRealmHandle, dictionary_value.dict_ptr); + var handle = new DictionaryHandle(realm.SharedRealmHandle, collection_ptr); return new RealmDictionary(realm, handle, null); } @@ -303,18 +300,6 @@ private struct LinkValue public readonly TableKey table_key; } - [StructLayout(LayoutKind.Sequential)] - private struct ListValue - { - public IntPtr list_ptr; - } - - [StructLayout(LayoutKind.Sequential)] - private struct DictionaryValue - { - public IntPtr dict_ptr; - } - [StructLayout(LayoutKind.Sequential)] private readonly struct TimestampValue { diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index d3133eaa12..31c5102b63 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -167,8 +167,7 @@ typedef struct realm_value { realm_uuid_t uuid; realm_link_t link; - realm_list_t list; - realm_dict_t dictionary; + void* collection; char data[16]; }; @@ -521,7 +520,7 @@ static inline realm_value_t to_capi(List* list) { realm_value_t val{}; val.type = realm_value_type::RLM_TYPE_LIST; - val.list.list_ptr = list; + val.collection = list; return val; } @@ -529,7 +528,7 @@ static inline realm_value_t to_capi(object_store::Dictionary* dictionary) { realm_value_t val{}; val.type = realm_value_type::RLM_TYPE_DICTIONARY; - val.dictionary.dictionary_ptr = dictionary; + val.collection = dictionary; return val; } From fac6d3a2ac856609a0b030946109b6f0b71639f4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:53:52 +0100 Subject: [PATCH 65/88] Changed return value --- .../DatabaseTypes/RealmCollectionBase.cs | 18 ----------- Realm/Realm/DatabaseTypes/RealmDictionary.cs | 4 +-- Realm/Realm/DatabaseTypes/RealmList.cs | 6 ++-- Realm/Realm/Handles/CollectionHandleBase.cs | 13 ++++++++ Realm/Realm/Handles/DictionaryHandle.cs | 8 ++--- Realm/Realm/Handles/ListHandle.cs | 12 +++---- Realm/Realm/Handles/ObjectHandle.cs | 4 +-- Realm/Realm/Helpers/CollectionHelpers.cs | 32 ++++++++++++------- 8 files changed, 50 insertions(+), 47 deletions(-) diff --git a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs index a0746f7e49..e8c1c09666 100644 --- a/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs +++ b/Realm/Realm/DatabaseTypes/RealmCollectionBase.cs @@ -262,24 +262,6 @@ private void UnsubscribeFromNotifications(NotificationCallbackDelegate callba _notificationCallbacks.Value.Remove(callback, shallow); } - protected void CreateInternalCollectionAndPopulate(RealmValue realmValue, IntPtr collectionPtr) - { - switch (realmValue.Type) - { - case RealmValueType.List: - var listHandle = new ListHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.PopulateList(Realm, listHandle, realmValue); - break; - case RealmValueType.Dictionary: - var dictionaryHandle = new DictionaryHandle(Realm.SharedRealmHandle, collectionPtr); - CollectionHelpers.PopulateDictionary(Realm, dictionaryHandle, realmValue); - break; - default: - Debug.Fail("Invalid collection type"); - break; - } - } - #region INotifyCollectionChanged private void OnChange(IRealmCollection sender, ChangeSet? change) diff --git a/Realm/Realm/DatabaseTypes/RealmDictionary.cs b/Realm/Realm/DatabaseTypes/RealmDictionary.cs index 7d68fffc54..692b29f242 100644 --- a/Realm/Realm/DatabaseTypes/RealmDictionary.cs +++ b/Realm/Realm/DatabaseTypes/RealmDictionary.cs @@ -67,7 +67,7 @@ public TValue this[string key] if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, _dictionaryHandle.SetCollection(key, realmValue.Type)); + CollectionHelpers.PopulateCollection(Realm, _dictionaryHandle.SetCollection(key, realmValue.Type), realmValue); return; } @@ -132,7 +132,7 @@ public void Add(string key, TValue value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, _dictionaryHandle.AddCollection(key, realmValue.Type)); + CollectionHelpers.PopulateCollection(Realm, _dictionaryHandle.AddCollection(key, realmValue.Type), realmValue); return; } diff --git a/Realm/Realm/DatabaseTypes/RealmList.cs b/Realm/Realm/DatabaseTypes/RealmList.cs index 5a9f23594b..62bfde4ec7 100644 --- a/Realm/Realm/DatabaseTypes/RealmList.cs +++ b/Realm/Realm/DatabaseTypes/RealmList.cs @@ -69,7 +69,7 @@ internal RealmList(Realm realm, ListHandle adoptedList, Metadata? metadata) : ba if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, _listHandle.SetCollection(index, realmValue.Type)); + CollectionHelpers.PopulateCollection(Realm, _listHandle.SetCollection(index, realmValue.Type), realmValue); return; } @@ -97,7 +97,7 @@ public void Add(T value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, _listHandle.AddCollection(realmValue.Type)); + CollectionHelpers.PopulateCollection(Realm, _listHandle.AddCollection(realmValue.Type), realmValue); return; } @@ -140,7 +140,7 @@ public void Insert(int index, T value) if (realmValue.Type.IsCollection()) { - CreateInternalCollectionAndPopulate(realmValue, _listHandle.InsertCollection(index, realmValue.Type)); + CollectionHelpers.PopulateCollection(Realm, _listHandle.InsertCollection(index, realmValue.Type), realmValue); return; } diff --git a/Realm/Realm/Handles/CollectionHandleBase.cs b/Realm/Realm/Handles/CollectionHandleBase.cs index aa30c427da..cd9497aaa4 100644 --- a/Realm/Realm/Handles/CollectionHandleBase.cs +++ b/Realm/Realm/Handles/CollectionHandleBase.cs @@ -54,6 +54,19 @@ public ResultsHandle GetFilteredResults(string query, QueryArgument[] arguments) return new ResultsHandle(Root!, ptr); } + protected CollectionHandleBase GetCollectionHandle(IntPtr collectionPtr, RealmValueType collectionType) + { + switch (collectionType) + { + case RealmValueType.List: + return new ListHandle(Root!, collectionPtr); + case RealmValueType.Dictionary: + return new DictionaryHandle(Root!, collectionPtr); + default: + throw new InvalidOperationException("Invalid collection type"); + } + } + public abstract CollectionHandleBase Freeze(SharedRealmHandle frozenRealmHandle); public abstract void Clear(); diff --git a/Realm/Realm/Handles/DictionaryHandle.cs b/Realm/Realm/Handles/DictionaryHandle.cs index 66974f8963..b292e961dd 100644 --- a/Realm/Realm/Handles/DictionaryHandle.cs +++ b/Realm/Realm/Handles/DictionaryHandle.cs @@ -247,7 +247,7 @@ public ObjectHandle SetEmbedded(string key) return new ObjectHandle(Root!, result); } - public IntPtr SetCollection(string key, RealmValueType collectionType) + public CollectionHandleBase SetCollection(string key, RealmValueType collectionType) { EnsureIsOpen(); @@ -258,7 +258,7 @@ public IntPtr SetCollection(string key, RealmValueType collectionType) keyHandles?.Dispose(); nativeException.ThrowIfNecessary(); - return result; + return GetCollectionHandle(result, collectionType); } public void Add(string key, in RealmValue value) @@ -290,7 +290,7 @@ public ObjectHandle AddEmbedded(string key) return new ObjectHandle(Root!, result); } - public IntPtr AddCollection(string key, RealmValueType collectionType) + public CollectionHandleBase AddCollection(string key, RealmValueType collectionType) { EnsureIsOpen(); @@ -301,7 +301,7 @@ public IntPtr AddCollection(string key, RealmValueType collectionType) keyHandles?.Dispose(); nativeException.ThrowIfNecessary(); - return result; + return GetCollectionHandle(result, collectionType); } public bool ContainsKey(string key) diff --git a/Realm/Realm/Handles/ListHandle.cs b/Realm/Realm/Handles/ListHandle.cs index d4726fb1dd..3c87335ace 100644 --- a/Realm/Realm/Handles/ListHandle.cs +++ b/Realm/Realm/Handles/ListHandle.cs @@ -149,13 +149,13 @@ public ObjectHandle AddEmbedded() return new ObjectHandle(Root!, result); } - public IntPtr AddCollection(RealmValueType collectionType) + public CollectionHandleBase AddCollection(RealmValueType collectionType) { EnsureIsOpen(); var collectionPtr = NativeMethods.add_collection(this, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return collectionPtr; + return GetCollectionHandle(collectionPtr, collectionType); } public void Set(int targetIndex, in RealmValue value) @@ -177,13 +177,13 @@ public ObjectHandle SetEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - public IntPtr SetCollection(int targetIndex, RealmValueType collectionType) + public CollectionHandleBase SetCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); var collectionPtr = NativeMethods.set_collection(this, (IntPtr)targetIndex, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return collectionPtr; + return GetCollectionHandle(collectionPtr, collectionType); } public void Insert(int targetIndex, in RealmValue value) @@ -205,13 +205,13 @@ public ObjectHandle InsertEmbedded(int targetIndex) return new ObjectHandle(Root!, result); } - public IntPtr InsertCollection(int targetIndex, RealmValueType collectionType) + public CollectionHandleBase InsertCollection(int targetIndex, RealmValueType collectionType) { EnsureIsOpen(); var collectionPtr = NativeMethods.insert_collection(this, (IntPtr)targetIndex, collectionType, out var nativeException); nativeException.ThrowIfNecessary(); - return collectionPtr; + return GetCollectionHandle(collectionPtr, collectionType); } public int Find(in RealmValue value) diff --git a/Realm/Realm/Handles/ObjectHandle.cs b/Realm/Realm/Handles/ObjectHandle.cs index 36a19ed6c7..ae8f098cd4 100644 --- a/Realm/Realm/Handles/ObjectHandle.cs +++ b/Realm/Realm/Handles/ObjectHandle.cs @@ -231,10 +231,10 @@ public void SetValue(string propertyName, Metadata metadata, in RealmValue value switch (value.Type) { case RealmValueType.List: - CollectionHelpers.PopulateList(realm, new ListHandle(Root!, collectionPtr), value); + CollectionHelpers.PopulateCollection(realm, new ListHandle(Root!, collectionPtr), value); break; case RealmValueType.Dictionary: - CollectionHelpers.PopulateDictionary(realm, new DictionaryHandle(Root!, collectionPtr), value); + CollectionHelpers.PopulateCollection(realm, new DictionaryHandle(Root!, collectionPtr), value); break; default: break; diff --git a/Realm/Realm/Helpers/CollectionHelpers.cs b/Realm/Realm/Helpers/CollectionHelpers.cs index bb50d9a842..8506f1b9a4 100644 --- a/Realm/Realm/Helpers/CollectionHelpers.cs +++ b/Realm/Realm/Helpers/CollectionHelpers.cs @@ -16,27 +16,35 @@ // //////////////////////////////////////////////////////////////////////////// +using System.Diagnostics; + namespace Realms.Helpers; internal static class CollectionHelpers { - internal static void PopulateList(Realm realm, ListHandle handle, RealmValue content) + internal static void PopulateCollection(Realm realm, CollectionHandleBase handle, RealmValue content) { - var newList = new RealmList(realm, handle, metadata: null); - - foreach (var item in content.AsList()) + if (handle is ListHandle listHandle) { - newList.Add(item); - } - } + var newList = new RealmList(realm, listHandle, metadata: null); - internal static void PopulateDictionary(Realm realm, DictionaryHandle handle, RealmValue content) - { - var newDict = new RealmDictionary(realm, handle, metadata: null); + foreach (var item in content.AsList()) + { + newList.Add(item); + } + } + else if (handle is DictionaryHandle dictHandle) + { + var newDict = new RealmDictionary(realm, dictHandle, metadata: null); - foreach (var item in content.AsDictionary()) + foreach (var item in content.AsDictionary()) + { + newDict.Add(item); + } + } + else { - newDict.Add(item); + Debug.Fail("Invalid collection type"); } } } From 90bb0bca8afd33c15ee1b82a5d16fa31b52d3085 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:16:35 +0100 Subject: [PATCH 66/88] Small corrections --- wrappers/src/marshalling.hpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/wrappers/src/marshalling.hpp b/wrappers/src/marshalling.hpp index 31c5102b63..0cc0d56198 100644 --- a/wrappers/src/marshalling.hpp +++ b/wrappers/src/marshalling.hpp @@ -137,14 +137,6 @@ typedef struct realm_link { TableKey table_key; } realm_link_t; -typedef struct realm_list { - List* list_ptr; -} realm_list_t; - -typedef struct realm_dict { - object_store::Dictionary* dictionary_ptr; -} realm_dict_t; - typedef struct realm_object_id { uint8_t bytes[12]; } realm_object_id_t; @@ -167,7 +159,7 @@ typedef struct realm_value { realm_uuid_t uuid; realm_link_t link; - void* collection; + object_store::Collection* collection; char data[16]; }; From 4d7a0560e30e3d1e4019daec4b59b46a352da854 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 14 Nov 2023 17:21:21 +0100 Subject: [PATCH 67/88] Fixed changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24c3da5a67..f3bdf3a315 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## vNext (TBD) ### Enhancements -* Added support for collections of `RealmValue` (`IList`, `ISet`, `IDictionary`) to be contained in a `RealmValue`. Lists and dictionaries can contain an arbitrary number of collections themselves, while sets cannot. It is possible to convert an existing collection to a `RealmValue` using the new static methods `RealmValue.List`, `RealmValue.Set` and `RealmValue.Dictionary` or using the implicit operators if converting from common types like `List`, `HashSet` or `Dictionary`. Finally, it is possible to obtain the contained collections by using the new conversion method `AsList`, `AsSet` and `AsDictionary`. For example: +* Added support for list and dictionaries of `RealmValue` (`IList` and `IDictionary`) to be contained in a `RealmValue`. Lists and dictionaries can contain an arbitrary number of collections themselves. It is possible to convert an existing collection to a `RealmValue` using the new static methods `RealmValue.List` and `RealmValue.Dictionary` or using the implicit operators if converting from common types like `List`, `RealmValue[]` or `Dictionary`. Finally, it is possible to obtain the contained collections by using the new conversion method `AsList` and `AsDictionary`. For example: ```csharp var list = new List { 1, true, "stringVal" }; From 0a5764135d8ae4596703624246054083a68c5bb0 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:09:04 +0100 Subject: [PATCH 68/88] Updated core and a couple of fixes --- Tests/Realm.Tests/Database/CollectionTests.cs | 4 +++- Tests/Realm.Tests/Database/RealmResults/SortingTests.cs | 2 +- wrappers/realm-core | 2 +- wrappers/src/app_cs.cpp | 2 +- wrappers/src/shared_realm_cs.cpp | 6 +++--- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Tests/Realm.Tests/Database/CollectionTests.cs b/Tests/Realm.Tests/Database/CollectionTests.cs index d1e14bdd1b..f1e158cdc7 100644 --- a/Tests/Realm.Tests/Database/CollectionTests.cs +++ b/Tests/Realm.Tests/Database/CollectionTests.cs @@ -1204,7 +1204,9 @@ public void QueryFilter_WithWrongArguments_ShouldThrow(StringQueryTestData data) }); var ex = Assert.Throws(() => _realm.All().Filter($"{data.PropertyName} = $0", data.NonMatchingValue))!; - Assert.That(ex.Message, Does.Contain($"Unsupported comparison")); + Assert.That(ex.Message, Does.Contain($"Cannot compare argument") + .Or.Contain("Unsupported comparison") + .Or.Contain("Cannot convert")); } [Test] diff --git a/Tests/Realm.Tests/Database/RealmResults/SortingTests.cs b/Tests/Realm.Tests/Database/RealmResults/SortingTests.cs index bea815ac36..4333db8a5a 100644 --- a/Tests/Realm.Tests/Database/RealmResults/SortingTests.cs +++ b/Tests/Realm.Tests/Database/RealmResults/SortingTests.cs @@ -348,7 +348,7 @@ public void SortsByAcceptedOrder() } }); var sortedCities = _realm.All().OrderBy(c => c.Name).ToList().Select(c => c.Name); - Assert.That(sortedCities, Is.EqualTo(new[] { "A-Place", "A Place", "Santo Domingo", "São Paulo", "Shanghai", "Sydney", "Åby" })); + Assert.That(sortedCities, Is.EqualTo(new[] { "A Place", "A-Place", "Santo Domingo", "Shanghai", "Sydney", "São Paulo", "Åby" })); } [TestCase(true)] diff --git a/wrappers/realm-core b/wrappers/realm-core index df25f80781..faf9c152af 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit df25f80781fbee4560bef9eeaaf993455be15047 +Subproject commit faf9c152af9449b622e28cc144c98ecda5faf656 diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index aae3cb6733..f95dd221e3 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -309,7 +309,7 @@ extern "C" { bool did_reset = false; while (!did_reset) { try { - app->sync_manager()->reset_for_testing(); + app->sync_manager()->tear_down_for_testing(); did_reset = true; } catch (...) { diff --git a/wrappers/src/shared_realm_cs.cpp b/wrappers/src/shared_realm_cs.cpp index 53dc338c9e..25661bfc71 100644 --- a/wrappers/src/shared_realm_cs.cpp +++ b/wrappers/src/shared_realm_cs.cpp @@ -97,7 +97,7 @@ namespace binding { class DotNetLogger : public Logger { protected: - void do_log(Level level, const std::string& message) override final + void do_log(const LogCategory& category, Level level, const std::string& message) override final { s_log_message(to_capi(message), level); } @@ -292,11 +292,11 @@ REALM_EXPORT void shared_realm_install_callbacks( realm::binding::s_can_call_managed = true; Logger::set_default_logger(std::make_shared()); - Logger::set_default_level_threshold(Logger::Level::info); + LogCategory::realm.set_default_level_threshold(Logger::Level::info); } REALM_EXPORT void shared_realm_set_log_level(Logger::Level level) { - Logger::set_default_level_threshold(level); + LogCategory::realm.set_default_level_threshold(level); } REALM_EXPORT SharedRealm* shared_realm_open(Configuration configuration, NativeException::Marshallable& ex) From 6057b25c41f56acdb65894aaa6b3c81224948408 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:39:36 +0100 Subject: [PATCH 69/88] Small corrections --- Tests/Realm.Tests/Database/NotificationTests.cs | 4 ++-- Tests/Realm.Tests/Database/RealmValueTests.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/Realm.Tests/Database/NotificationTests.cs b/Tests/Realm.Tests/Database/NotificationTests.cs index 7a75955ebe..33a21b73c2 100644 --- a/Tests/Realm.Tests/Database/NotificationTests.cs +++ b/Tests/Realm.Tests/Database/NotificationTests.cs @@ -1593,7 +1593,7 @@ public void DictionaryOfObjects_SubscribeForNotifications_DoesntReceiveModificat dict.Clear(); }); - VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: false); + VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: true); } [Test] @@ -1641,7 +1641,7 @@ public void DictionaryOfPrimitives_SubscribeForNotifications_ShallowHasNoEffect( dict.Clear(); }); - VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: false); + VerifyNotifications(changesets, expectedDeleted: new[] { 0 }, expectedCleared: true); } #region Keypath filtering diff --git a/Tests/Realm.Tests/Database/RealmValueTests.cs b/Tests/Realm.Tests/Database/RealmValueTests.cs index d8fc5c1b44..cfcc7b3bd3 100644 --- a/Tests/Realm.Tests/Database/RealmValueTests.cs +++ b/Tests/Realm.Tests/Database/RealmValueTests.cs @@ -1161,7 +1161,7 @@ private static string ConvertRealmValueTypeToFilterAttribute(RealmValueType rvt) RealmValueType.Double => "double", RealmValueType.Decimal128 => "decimal", RealmValueType.ObjectId => "objectid", - RealmValueType.Object => "object", + RealmValueType.Object => "objectlink", RealmValueType.Guid => "uuid", _ => throw new NotImplementedException(), }; From c79f63d3a5a560a3449603e56ae8e0aa0a8d87ca Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:39:53 +0100 Subject: [PATCH 70/88] Updated dbs --- .../ForMigrationsToCopyAndMigrate.realm | Bin 8192 -> 8192 bytes .../Realm.Tests/EmbeddedResources/v6db.realm | Bin 1835008 -> 1835008 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm b/Tests/Realm.Tests/EmbeddedResources/ForMigrationsToCopyAndMigrate.realm index e8c0207e3e87f8ff90b5860ac3365da250ad762b..2b30bff65389205936dc006c399507744220c525 100644 GIT binary patch literal 8192 zcmeHLJ!~7v6@Ih3Bv;EHtz^exY#hQ<8a4q`vfR7aJ{{mY6K5w;C-EdkVyqv6WO6#- z0JBIT1SwRyRLQsqSQ@xS2vQIT0vM^(IAA(97F4PTPWiss8IszQ?hY_g1mIA+kMriu zo9}z`=7;ici!T1HSU&yv{SWWViF|;P7jbs$o!VxtQ;SsfGDUnI{Dfg_#PPr*cLAmEr=;X7NlDpXiOIug!r#Ut?%9<{f0;=5 zNe$F%b*hGHq+Wh?e*E$)r-cqz}BNJ4&C!foY%O9q9zK`g6Guqy)-} z224HH2*+j7_~1V`0ND#aBmNxoC!8x|t+UYH#=%Ofk4!t)3w12CPu6WbZvW*^zh*mm zdtbgRS$mA{taz*5e=-uW9ePCxy?YUdS^Lc<#-ZJ@`@0gGaSaqw~8>tI9mu-`ko}q(5x5I@_DI@xJ(Ax7OI1>i=>3FMGVH*mV&w_nHm_v0`2>U#I_*bj-6 zZa%ZFu9fd4{3eZloH!Sf?ROI6Cfjev?Nu<;n{l%J*89J#-v8j~gSF~sk3ahC!ILKm zgL(2mDqqP@<|F?U9=1m&-_Tc0*O3lt!#Jd#d5Rnu=L70E4=&R7JV+(ZgX{7<%0=V; zNn93(mGqC3Mpm37uasNPRWs|Es=MwqGY1*s0jcr%V({gLaN5nx$zj0r=_nWkK#an6 z44eXmKd=t&S|SQv)?F!R=^07;2zpB3NMq%Ap;z=uUVN;Y_7o*}rp%h-x_izM<54H$ z+Vz-}N^*aLIc6e1prk+YzVObKJ11{#(9$z9g=wK(+Sq4MW8F9PImzo|AdrIA6F28IOqEjG zdav*5H}y6yRsw{^gMIyqL{-rqQtRQ*DFtPHg`?i~x9zqb8G9=kADo|a68J$F6ocg; z3RZ$@uo|of^aHyrCE}lDjKA+eJcSPn`(b|AkL9_3zk{^#v+<;V;vdc(y4yj#?p(d@ zCgUSl+&ghqPzj~-qqWmv4Qx}{^!4~Z*J>f*lK);P%f2Vd*Mwo_i9?+#OwEqF;zFt3O9>({c z>GhMGANu`Bq%cY#@y!CX>SG*oEEbTxcUPITDBDKrB#%}4t;FlnmF;JnIBqhd0Ns!~b^P#ipInd4sxp>~!N{M+(+WpV( zEy)2B^^(okRHEU#@)fr8k(BWJWW}kvBe&~~yg@3$twC~!xn4dB*Y&-DcFPhAGt{1*KQ1hKxx zVp5Q_M}4t~`%7Pj_|@b)MAdVTykjrMB>7&xGCvAAjKFyh01&s_VGjC;J!DFn~@EHyojh4@qJtKso?F)5$Xr}H(5`r1=06x@VNK( z&D%44{&jYCR(K1H(>%&ZXz~~Ve;H$L<9Y9T9(TzjR987%Wzf&W9QyfJg(w__2aDjL z_aVgP?u5sL!&L?)!{JB{RS*A7Im}lJmzTr~9(RYsv*8*3zB&75$kdqTG0q#{KgYAY zDDuC^N@x|BKA{~zO=tYoV>a_Y|a7Y-IHw|V|^eE46AIIm#E915{cU*J2lYa8_n zJ_Bfr_)ZJ5XP%wPy0TnPaIUWcEB%4*k}QPyu%Lr1@DtY{p$RcPvTp3j{}pnO3zhsU vw?q!{oZ|TzY{-8ErwVdbW)YZ0U>1Q{1ZEMKMPL?zSp;Sgm_^|KF9QDsh=zEW literal 8192 zcmeHKJ8vUZ6uysn_`$LROW|^~l^Y~?q{D0;c%`sJikEel4E7LP8$UE2HoGVi(-9Cw z%9JicSRt1wS5z)7suUEVO9OIgArO4$&LfjZ`2~A#q&aiWJ@-7mbLQrFYBRMO32lD3 z_NZu*C}Q^*Ge^(k{p5MC?}uK;3%!K5+xy;lyt6jkiv?E9e||9V1CkgqzvTzx;Q+iO zyMi%MiuLhSe#GecPUwBP?a5Q4;urmTZB?)NZ7RA1J!)Dn& z@l!TeT8SM_^c}|d3q;map+&T>;(Xu)RMy-gF zG#~!)ZpH&g`NZq{iGWCXb+|uN2*twE7hb#F8+f7LQ38|VGhoMYVg=$?fx}17{{AMG zC>)F}zROSesfznH4yj&Uy+kaf$j6-VIlthS{ED9)E+%J(WS{ap=L^`PrSg!!BKUYI zywW^mb2y@+b3De_S9L|aG7C43S3G4mhebsC%$d`%iqo zGj{tb3+lhS?2jbHpV`msxk;6%PUvH-NIi+(#x>>ZO5;W!lqd33PUX3LBWH3hFXW}X zk_#1U+z-ccsW^&{zL&&_iuX2;Fa;p|_Jd!3d`9}kZw?20If3=47lhr8m-X*@-|Ow?&OaVM9)y12kHg*ocdKXyy}do$ zRom^Z-}e~b2zvgYL!X{n1&SLumgD=vVd-~B@-^gLEIuH`v@d~?YtGC|llD;ro)(sP zQX~p|1hX8JF*}#*mx^`WFK(v4Fspg}tX~_}&eLre`uEQRVFGuTPsI$qRW+vgj;`f3 zwm|<<)pprRF_xnq_UhUyg{96TF4V1Mad%`LSrZFt%0FnDMvmW7_ZsC>$9_taILbBL z5><5n)L_&yXOy6><%34E``wMWP9?7|t%|*3 zr~e<2Z|O4UWogZA@fJQ(gpb^jrXaqC@cO#I_IWBkPMEwR>s#$1honS`E&q2qmipi{s*^FkP z3F{V4a&yqe`g)TFKeC7U1oPQa@H%r`r|!^C$iz>{&>)HP9eD#h-KqH_L&Y>H<`{vF_6nDMY=BFclJo9^I=A1KU=FYvRb&TNHGn`4VgK_4)PR2rPsI->A zp6cKZzN%jHJ;^r>z5)^wT7m=LcTP2Yq^-4|`3g?ejSg`C>k=XnLKeef5hN)JU&L zh{r@mTozUFs@N8{#P`GxMZlOe=8RKD#<*ZSZ*1SbeOrw_l>c*AJ0|RH6e0`19+eWL zq)o)L0IW_pS%0j8F^$~5&E%oo+_4~vFh7QmTM(F=4EMPpI@RgCGZh*RSgEA$Yg}~!auzoO}GK0pz~JXbc~F=VyEXI)Eu;?EGuc* zRxltEwSk?4lkSQ27liNy$NR;A7;+YR9)Acd{p*X%XI8EivdfEw^M&QL^VbUN7r5rx z6IdIMee&l%`STdTE8f`a-|yKqINTAhNREW9R&P1c?oExQ$Luj-2F8m>7$H1J2o(|CJ>)v`~6-e_|)#74?K6{9-?`4GUvnId#N! zTZeMG*DaOMy>3ICF&knISjbkyIXz!>Oq1GZK+5rUrfuipc&tbC&i7+NPJQel=UFR!n&N8vGb6UIS2U)n zy4uv{)RDOKW(02DwKJ=0Yv)$i3eT*ru{XyWkM|EUCPZ?wIEfu>Tsqb(<7sCm9v(&` zW9jJb8SGf>d*%Wqo4#_zg80LP4e>bpgcN${|l)Y8&hMbEH1-gARf6u$` zJ&^egxf8HGiVaez(A$PT$lSt_Vd99t4Gpqftw-mNCO@y;8^qk#U`<0&kMP}M+7%lX zj1(U>4n+fs_9z-uv{%tSMf(*EDLSC&pi3)aNM(c-9aeNi(NRSsijFBduIPlKoEQ{g zYN2;i=#%~%A>?zV9(uwM70xhpOJ%$yw^pP{JtFi31&ne@eTw=O4Jg{9Xi(8!Mf()( zS2U#PfTDw>bV5TaBdjQA0u}ee8H`peRM0jIN8xF3s8kZEM^QZm3{g(Suj;|bW#|!x ztREB}S>CJK^{I5fq9H}8B=U!y{%&N%2s)pSOjNWVqq%X2I^9ieRgNhP8Q93vGxm%f z%fzy=Tr3|e#!9hrtQxDuHexOx)E^{240aGot7uU6~r7JS({(>>h$rX{iAs)cBGFUji^GPKI%5p!DMm)n~eMe9OMiqBJ zOmkhA`)Yj~IQ9#%1E+l`Au<1`TLCV!)mdP$AtYdQIUz&Zg7ZJ|ISlpCvC3`jEXKa2 zhSLP4hu8zv!)+nnR^=Q5gYHFo9F<0o=w+~>%h8@JWMF;nk}IVm`#)3=Dsb7kX`PRG zV_JKM{>@ec=CnFHq9p zgA-&frowWr=m3YuK2-2-j@QQOQ_g#F>`r*lTAmDIcMIZtvE{rPk2nYMkP}Im1*)!! zjXOMqM!91mzxa8jjM3dbbOw8y!Th|X#q zXb#v;47HWWmh%|2$N3-;?WdNoHmGJCj>jsW!5C|5445l)BIJYfF07inA0P9$LdcEd z&nH}KfEaM>gx#xSXHVC$ljT{I>qiH_IR5B>5gD*7=knw*PXU-od1}9%z1h`xWWW^ zKLsz>eRHMnbLLnHq5J0QQ*O4n?+10~L*J&+gWq*xX!wAr?fQ$!_qfcCIM7Y-r~{3Tfo`Y) zfz`2rHjpnxb+3`?CkxeqJh~i!dH2zU1O2$83uX+X^GFrpl1EZ31kwtA+zg+hI6cb> z0Y$ZL^$bm{(Il`1=#Yk<^#JR*v1KrEYa0j6f>JGhAcuGjI0~>w)zBl<0@BYdtS>Aqtk=<-8UVHlk4<$!6?TP@|CeVi^mCU-t)jHr z6b@7wprp1i)D;aJVmRWbusSwyYtk8ZRazd!QP5WJWvFKc>YAag+r2H{BH)qVCl5sA z%T;INR-d5?ngHE{9cf$HLf*D2XC3CV9ZiE~z@XFeB!zSnH#)ljZ+2S1KF|hO&xWxN zGXm8OLmO1Pls+}^qX5k$W7rh=F;wI)=-uxDVC)}r5%3ee2cTqmi_CEB7MaeUE8#BJ z$Z{q)F?b9M6%Z&X%nVzer%LGm9FR zA2IM!pA9rKmZZSbH?(jNCg$(wbg{V9KhBx5J4URK^<_H&#~6zu|gKnvIh+Q0!I;46bbSousPqdq00J}y^fb~L>^Ko=bbSRcyl z^N2y4$3j*2$X=1bP?5j)pxO#R`D#=$rn%ASH!Gav|yu7o&i@Bi%LRSZY z127L_)ten2A&EF=Msv%LRmYq^i1t4ov8J>|heyQfXWwP|&` zCk`6jtnf{Q&#;(0lsmFKQtp^lhGNrt!1bcc=K#5=>X_jUeOt%t;4tDW$1Htf+!{}g zr^cD)qQV7wj5GFgR7F%Q0cD^HFtDPHhNJ;d>X_Rq7Zxe=VHnJ3s9<1K4)ADggkEBr z_0W%ePn`pAzMejb{QA4m%ArX3E*kHl+4?Z34qyi!$=JZbPDwzf72`mD9J>!t19V8@ zT`$WqbMMah-gpaTOlx<-dRSC%NBSrO`Gi|WlPXYCO>Hz~6U^Pq`EkMMJ&yU9JM$T^ z7r4sMN@6+ZoB^*Qz*pz9XkQ1_Rf%a) zuj(sfYtR2y;qvwMg_WhN*YINN$`J<{8(Lmk{p`n=X)V8N3E4pcxn=~5)#jqDWid~p%12f&h4MG~? z*~b?U)i`dWfR;KUvcA>;B~6G&6{AiSpn#zwe?i%BSQwKlh3kt~7OyXg8+r3ab!_DF z^5XjXLZ+~|e7$&X?Mh);+-O8^v`)NYL>$YAIW}nCm~^U!dD^XeVRh;Hx?9!Fc&@N` zy;!*BW_g7#gDZd4-Kig=yNmbJom*#N;S8rCbShi8wz9OozO=Rq|E$l8nEizxV>p$c z$Z+%s&qLrVhvPlJwhW_i%@4zT{&1LX_4&)!mp*snvjhJ}ef`u&tS56}y>P8V(6G-W z-?mA2X2G&8KU^xnMZxckX#9L6;mr-tpZhR+TixPq;9}0kg}#0fXu(dAdbS8Ru+BS%+Y}22=sIA@T}!P?p=`Gj7C0q72loM1(!tgUe;NK?!tE z#o3t!>_S?3NNNuQBJyyDTuR7Ft|HwDA=Z$yX5br|2R_AUXVB?YgE6u=i$hE9YnVU$ zseuvid^0NYe}h92m+W+gzv4C|=53!$O>5XF8|AcnzomX@xu}Q3rxznM!jTv2hu=U!6%^DZNo=9eSIC zjYFmcLrE&c)!Mw)l3U~|)UE24bn4B5X;5#p<1}tdTfm1>Z$9h|5m-UouwJum4`UUJ zO`*?AoXIZwu0ripJ=r9&rSY|HPU+Y}MZTSH0%>d_n?Mu5M<`+khkJe^#C03QWl0I( zS7v+Yv<+BT%lsNG%?B8u0klRc=oRO78cZ3;W06;Z7Es4x-%|^I6N|nHm>6Xdr~yGZ zfFw`^=nVLA2pxj3@GcCXBM2ujNMH}3QwS0#Q3c@V1*ysmIs^^?dWaxA1ib_o06j&H zZ>8WV4gkJlNZ}rvPd1!csyfeesSz(hp~umG0pIP;T$1u$L(k~imn!#BFb=0PR_`iml9sW{Ut zBP^h#;!IZovSOW_qG?b|#aWkSL=vdoypb?QD|JMRDw{=Q#b!EgA)W=wfNTc$#xlCN zIt*iy4yxPhvd(lzymHvlfoe?O$W^3$qnSbh0xcbeeCY+0y}US~%fq(y?+`0y zDlAwlTWtUoV+q((aSQZryGZyK4kb0mU1Y^@_mUOF8DkEAb#NYkaOM+krK6E?`Eax5 z!03}pFm)4q>G$Y|#vl0^e*SaSDB{okWlS@?s$$G>!H+*HtjI=p?<9{71UjJO|3J;2 zPJ_G_@k1~?4@xW={B!d>36Yy;NK#2aIyhJDlc zRlFq1TnSZj$R)xGCTI<)0ybbNx&=x_v0heD#>u&Jn_-7db(Glju zaJD`ivXuQla?!YiD#S&Df0{CWN#HV^qA;B{wo--~i96)5peN({$6K2w4}=u84MDJv@D!;zR}5aHrx9#S;j=K*cwy z?){sM-RJ;2Q4>2)5<3mQ=}Tc#sR4X^+Jrj!2t^|`VJ8`&2+#v<0wuMnY0p@=PMH1M=GaH}VxY_U|lllfW z1as2FO`}z5$FNvR1yK}oXa|`XfEsWQame3Cri6RA5R1Yo1mH05z zP=XND5`WAq(=fVIZ76CZR3RfY7O4_75}<(iBSJ%qrb>%Y|1c6l(LkF8)5ss7@;mR@ z`Jy-Z<-YUIx##@ux#!+@Q>#^LwW_=Hipi)I-f;Z>;*sf7Lqm+6M2j+(yjOn1DT}-& zo)4d9Osa_uu_N*ibCdi{%{J#5QRr6q0#Cc4b>G z%KW_Co}OmxkRl8T!$M3;32DJm9L*VWh8^9BIdLcHq@1)f;yBKjlX0>E-GRqhxgxL0 zJ(;(q9KWO(xiV%;O0VC?8H*`Q59>_Al%s;62&}ns$8?{`U;40j@_X-)@yRoc*=4w{ zk26-wFy^A41qy)YGByKrpkDztUN-srBV#thvjNyZE6vyvP*RXcS2Z8MLK#NefO61E z8HYW`zUr^hNn}GH#TAENRzEf!WNTKLQ(_tsqHLAbs#;fDD#;$?J-yPGw|d@Hr}}B% z?Wn8v)R1Otyrhi|!?Re>JX&^sSqmaR)OHUjYgMhTwR|Qr))7hc+s=rXfg-9;vrFu= z>~2@PFXXU}vKH5copb1FJ*_pgJVYZS(DS2*K4OZekoF~ri#AdVpz+vuMw%Np%$!t4 zes52uB$s`LFNE_nBk{Jj+wEew9&UtNVcrRkhG<6Z@JjgZgK$eY3>L!39`kOv7oLk$ zBgBGu3v$4lmTKC=3wl4Ol;NOKn4k*&Qei2P1GFo_R-;F<2Y=zj1};QU(oN{csf2Pk*yNqy=ZQT{4B*o6YTC-Y(abmd5$dvZL0)} zv6)yk-iXtDsbp8P*caFz1Njv56UpC-jEy2m0=9tPNbeV#Qpi>_8)-_kLz!uZkH5$6o=6@LD7(u3F)1r8ENsOs zBuh!poHKdNoZ_#TA^vN#=+|MzTs8Rzrgsg@AHjaNmrXO2DyF;?u^&X=$-a`>w)gt% zh_ zu;;Fo8B>^LSguvDiq?|ZHkVCeP7HdflEp7s-V{Pe`}a$`LX@qlRkvCe89yq-iWS)N zWz#$iZ*s?4wbre!KY+Zqtqp51KO{>2LpG>F-U{a)Cxeb=`;|u#WGuT#)@US?ektCa zDBxJpl6n5usBw56Hbyo$17?B}_H748fEFA9s(|kom9FU_bL4DCFVoJ7ZtW;v%U@6I z<4@SeL9wkB?1yOdniwfW2hBIz;f%Yn^ics8ej_a=s8E)ukw{z@`@>1bu_N*LA^Qgn zIfCbbW5$;%u0aN;G2vQdHveCQC-ONDDI>+8M_J%EX!M@E;9M@iBY$2uOyc`+hu*_@ z-WaJ1o#v-rcbqtV2ytq#IiR(lvCZb6Zkf)7+RG+=8<~CGWEG&$G@1KDlP%xGyIUrk z`WbQpZbU?;?AT|^c)u@oU3P`iWh&rj_WXO!mO zZkPweio#!Am{bc1R8pe(?hSK7Tp6br65UEKPo>mNBxs2| z$|jc3U^wNCcT+xNU&f)eLyp$G7ry!QnfWjF?W0Tx%6?16@xZ=&A^*~aya0%R1jv8_ zsDK89fFU3Z3Fn~Ca0Fw7EU9tk(7VqEGTp~1^?c> eG Date: Tue, 27 Feb 2024 15:32:03 +0100 Subject: [PATCH 71/88] Updated core to 14.0.1 and changelog --- CHANGELOG.md | 28 ++++++++++++++++++++++++---- wrappers/realm-core | 2 +- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de0045eeaf..9df0682b1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,13 @@ ## vNext (TBD) +**File format version bumped. Old files will be automatically upgraded but cannot be downgraded and opened with older versions of the .NET SDK.** + ### Breaking Changes * Added automatic serialization and deserialization of Realm classes when using methods on `MongoClient.Collection`, without the need to annotate classes with `MongoDB.Bson`attributes. This feature required to change the default serialization for various types (including `DateTimeOffset`). If you prefer to use the previous serialization, you need to call `Realm.SetLegacySerialization` before any kind of serialization is done, otherwise it may not work as epxected. [#3459](https://github.com/realm/realm-dotnet/pull/3459) +* Support for upgrading from Realm files produced by RealmCore v5.23.9 (Realm .NET v5.0.1) or earlier is no longer supported. (Core 14.0.0-beta.0) +* `String` and `byte[]` are now strongly typed for comparisons and queries. This change is especially relevant when querying for a string constant on a `RealmValue` property, as now only strings will be returned. If searching for binary data is desired, then that type must be specified by the constant. In RQL (`.Filter()`) the new way to specify a binary constant is to use `RealmValueProp = bin('xyz')` or `RealmValueProp = binary('xyz')`. (Core 14.0.0-beta.0) +* Sorting order of strings has changed to use standard unicode codepoint order instead of grouping similar english letters together. A noticeable change will be from "aAbBzZ" to "ABZabz". (Core 14.0.0-beta.0) +* In RQL (`Filter()`), ff you want to query using `@type` operation, you must use `objectlink` to match links to objects. `object` is reserved for dictionary types. (Core 14.0.0) ### Enhancements * Add support for passing a key paths collection (`KeyPathsCollection`) when using `IRealmCollection.SubscribeForNotifications`. Passing a `KeyPathsCollection` allows to specify which changes in properties should raise a notification. @@ -11,7 +17,6 @@ - building it implicitly with the conversion from a `List` or array of `KeyPath` or strings; - getting one of the static values `Full` and `Shallow` for full and shallow notifications respectively. - For example: ```csharp var query = realm.All(); @@ -26,15 +31,30 @@ ``` (PR [#3501 ](https://github.com/realm/realm-dotnet/pull/3501)) * Added the `MongoClient.GetCollection` method to get a collection of documents from MongoDB that can be deserialized in Realm objects. This methods works the same as `MongoClient.GetDatabase(dbName).GetCollection(collectionName)`, but the database name and collection name are automatically derived from the Realm object class. [#3414](https://github.com/realm/realm-dotnet/pull/3414) +* Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaulated as less than everything, previously nulls were not matched. (Core 13.27.0) +* Updated bundled OpenSSL version to 3.2.0. (Core 13.27.0) +* Storage of Decimal128 properties has been optimised so that the individual values will take up 0 bits (if all nulls), 32 bits, 64 bits or 128 bits depending on what is needed. (Core 14.0.0-beta.0) +* Querying a specific entry in a collection (in particular 'first and 'last') is supported in RQL (`.Filter()`). (Core 14.0.0-beta.0) ### Fixed -* None +* Fixed RQL (`.Filter()`) queries like `indexed_property == NONE {x}` which mistakenly matched on only x instead of not x. This only applies when an indexed property with equality (==, or IN) matches with `NONE` on a list of one item. If the constant list contained more than one value then it was working correctly. (Core 13.27.0) +* Uploading the changesets recovered during an automatic client reset recovery may lead to 'Bad server version' errors and a new client reset. (Core 13.27.0) +* Fixed crash in fulltext index using prefix search with no matches. (Core 13.27.0) +* Fixed a crash with Assertion `failed: m_initiated` during sync session startup. (Core 13.27.0) +* Fixed a TSAN violation where the user thread could race to read m_finalized with the sync event loop. (Core 13.27.0) +* Fix a minor race condition when backing up Realm files before a client reset which could have lead to overwriting an existing file. (Core 13.27.0) +* Boolean property `ChangeSet.IsCleared` that is true when the collection gets cleared is now also raised for `IDictionary`, aligning it to `ISet` and `IList`. (Core 14.0.0-beta.0) +* Fixed equality queries on `RealmValue` properties with an index. (Core 14.0.0-beta.0) +* Fixed a crash that would happen when more than 8388606 were pointing to a specific object. +* Fixed wrong results when querying for `NULL` value in `IDictionary`. (Core 14.0.0-beta.0) +* A Realm generated on a non-apple ARM 64 device and copied to another platform (and vice-versa) were non-portable due to a sorting order difference. This impacts strings or binaries that have their first difference at a non-ascii character. These items may not be found in a set, or in an indexed column if the strings had a long common prefix (> 200 characters). (Core 14.0.0-beta.0) + ### Compatibility -* Realm Studio: 13.0.0 or later. +* Realm Studio: 13.0.0 or later ?????????? TODO NEED TO CHECK --------------- ### Internal -* Using Core x.y.z. +* Using Core 14.0.1. ## 11.7.0 (2024-02-05) diff --git a/wrappers/realm-core b/wrappers/realm-core index faf9c152af..2bfd5e9ba7 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit faf9c152af9449b622e28cc144c98ecda5faf656 +Subproject commit 2bfd5e9ba74ed2620657bf968d2931f6ceab6785 From 3db20b5a0acd017f1dea320f679f5e054e6187f5 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:18:53 +0100 Subject: [PATCH 72/88] Added test for collection indexes --- Tests/Realm.Tests/Database/CollectionTests.cs | 127 ++++++++++++++++++ .../Database/RealmDictionaryTests.cs | 60 +++++++++ 2 files changed, 187 insertions(+) diff --git a/Tests/Realm.Tests/Database/CollectionTests.cs b/Tests/Realm.Tests/Database/CollectionTests.cs index f1e158cdc7..d375801a63 100644 --- a/Tests/Realm.Tests/Database/CollectionTests.cs +++ b/Tests/Realm.Tests/Database/CollectionTests.cs @@ -589,6 +589,73 @@ public void ListFilter_CanBeFilteredWithStringPredicate() Assert.That(rDogs.ElementAt(2).Name, Is.EqualTo("Rick")); } + [Test] + public void ListFilter_SupportsCollectionIndexes() + { + var joe = new Owner + { + Name = "Joe", + ListOfDogs = + { + new Dog { Name = "Fluffy" }, + new Dog { Name = "Plumpy" }, + } + }; + + var mark = new Owner + { + Name = "Mark", + ListOfDogs = + { + new Dog { Name = "Plumpy" }, + new Dog { Name = "Cuddly" }, + new Dog { Name = "Fluffy" }, + } + }; + + var anton = new Owner + { + Name = "Anton", + }; + + _realm.Write(() => + { + _realm.Add(joe); + _realm.Add(mark); + _realm.Add(anton); + }); + + var owners = _realm.All(); + + var query = owners.Filter("ListOfDogs[0].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(joe.Name)); + + query = owners.Filter("ListOfDogs[FIRST].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(joe.Name)); + + query = owners.Filter("ListOfDogs[LAST].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(mark.Name)); + + query = owners.Filter("ListOfDogs[LAST].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(mark.Name)); + + query = owners.Filter("ListOfDogs[*].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(2)); + Assert.That(query.ToArray().Select(p => p.Name).OrderBy(p => p), Is.EqualTo(new[] { joe.Name, mark.Name })); + + query = owners.Filter("ListOfDogs[SIZE] = $0", 0); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(anton.Name)); + + query = owners.Filter("ListOfDogs[SIZE] = $0", 3); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(mark.Name)); + } + [Test] public void ListFilter_WhenNotRealmList_Throws() { @@ -903,6 +970,66 @@ public void SetFilter_CanBeFilteredWithStringPredicate() Assert.That(rDogs.ElementAt(2).Name, Is.EqualTo("Rick")); } + [Test] + public void SetFilter_SupportsCollectionIndexes() + { + var joe = new Owner + { + Name = "Joe", + SetOfDogs = + { + new Dog { Name = "Fluffy" }, + new Dog { Name = "Plumpy" }, + } + }; + + var mark = new Owner + { + Name = "Mark", + SetOfDogs = + { + new Dog { Name = "Plumpy" }, + new Dog { Name = "Cuddly" }, + new Dog { Name = "Fluffy" }, + } + }; + + var anton = new Owner + { + Name = "Anton", + }; + + _realm.Write(() => + { + _realm.Add(joe); + _realm.Add(mark); + _realm.Add(anton); + }); + + var owners = _realm.All(); + + var query = owners.Filter("SetOfDogs[SIZE] = $0", 0); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(anton.Name)); + + query = owners.Filter("SetOfDogs[SIZE] = $0", 3); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(mark.Name)); + + query = owners.Filter("SetOfDogs[*].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(2)); + Assert.That(query.ToArray().Select(p => p.Name).OrderBy(p => p), Is.EqualTo(new[] { joe.Name, mark.Name })); + + Assert.That(() => owners.Filter("SetOfDogs[FIRST].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[FIRST] not expected")); + + Assert.That(() => owners.Filter("SetOfDogs[LAST].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[LAST] not expected")); + + Assert.That(() => owners.Filter("SetOfDogs[2].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[2] not expected")); + } + [Test] public void SetFilter_WhenNotRealmSet_Throws() { diff --git a/Tests/Realm.Tests/Database/RealmDictionaryTests.cs b/Tests/Realm.Tests/Database/RealmDictionaryTests.cs index 9508c967e7..0681d09fd0 100644 --- a/Tests/Realm.Tests/Database/RealmDictionaryTests.cs +++ b/Tests/Realm.Tests/Database/RealmDictionaryTests.cs @@ -1313,6 +1313,66 @@ public void DictionaryFilter_NoArguments_Throws() Throws.TypeOf().And.Message.Contains("Request for argument at index 0 but no arguments are provided")); } + [Test] + public void DictionaryFilter_SupportsCollectionIndexes() + { + var joe = new Owner + { + Name = "Joe", + DictOfDogs = + { + { "a", new Dog { Name = "Fluffy" } }, + { "b", new Dog { Name = "Plumpy" } }, + } + }; + + var mark = new Owner + { + Name = "Mark", + DictOfDogs = + { + { "a", new Dog { Name = "Plumpy" } }, + { "b", new Dog { Name = "Cuddly" } }, + { "c", new Dog { Name = "Fluffy" } }, + } + }; + + var anton = new Owner + { + Name = "Anton", + }; + + _realm.Write(() => + { + _realm.Add(joe); + _realm.Add(mark); + _realm.Add(anton); + }); + + var owners = _realm.All(); + + var query = owners.Filter("DictOfDogs[SIZE] = $0", 0); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(anton.Name)); + + query = owners.Filter("DictOfDogs[SIZE] = $0", 3); + Assert.That(query.Count, Is.EqualTo(1)); + Assert.That(query.Single().Name, Is.EqualTo(mark.Name)); + + query = owners.Filter("DictOfDogs[*].Name = $0", "Fluffy"); + Assert.That(query.Count, Is.EqualTo(2)); + Assert.That(query.ToArray().Select(p => p.Name).OrderBy(p => p), Is.EqualTo(new[] { joe.Name, mark.Name })); + + Assert.That(() => owners.Filter("DictOfDogs[FIRST].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[FIRST] not expected")); + + Assert.That(() => owners.Filter("DictOfDogs[LAST].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[LAST] not expected")); + + Assert.That(() => owners.Filter("DictOfDogs[2].Name = $0", "Fluffy"), Throws.TypeOf() + .And.Message.Contains("[2] not expected")); + } + private static void RunUnmanagedTests(Func> accessor, TestCaseData testData) { TestHelpers.RunAsyncTest(async () => From 82c6b5061433942a8824082709b1e7f478065d25 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:26:27 +0100 Subject: [PATCH 73/88] Updated changelog --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9df0682b1e..91622c95bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,27 @@ * Updated bundled OpenSSL version to 3.2.0. (Core 13.27.0) * Storage of Decimal128 properties has been optimised so that the individual values will take up 0 bits (if all nulls), 32 bits, 64 bits or 128 bits depending on what is needed. (Core 14.0.0-beta.0) * Querying a specific entry in a collection (in particular 'first and 'last') is supported in RQL (`.Filter()`). (Core 14.0.0-beta.0) +* Add support for collection indexes in RQL (`Filter()`) queries. + For example: + ```csharp + var people = realm.All(); + + //People whose first dog is called "Fluffy" + var query1 = people.Filter("ListOfDogs[FIRST].Name = $0", "Fluffy") + + //People whose last dog is called "Fluffy" + var query2 = people.Filter("ListOfDogs[LAST].Name = $0", "Fluffy") + + //People whose second dog is called "Fluffy" + var query3 = people.Filter("ListOfDogs[2].Name = $0", "Fluffy") + + //People that have a dog called "Fluffy" + var query4 = people.Filter("ListOfDogs[*].Name = $0", "Fluffy") + + //People that have 3 dogs + var query5 = people.Filter("ListOfDogs[SIZE] = $0", 3) + ``` + (Core 14.0.0) ### Fixed * Fixed RQL (`.Filter()`) queries like `indexed_property == NONE {x}` which mistakenly matched on only x instead of not x. This only applies when an indexed property with equality (==, or IN) matches with `NONE` on a list of one item. If the constant list contained more than one value then it was working correctly. (Core 13.27.0) From ee596a5d21f516d9050df063203935059707b0a4 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:33:56 +0100 Subject: [PATCH 74/88] Fixed realm studio version --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91622c95bc..73f05a0021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,7 +72,7 @@ ### Compatibility -* Realm Studio: 13.0.0 or later ?????????? TODO NEED TO CHECK --------------- +* Realm Studio: 15.0.0 or later !!!!!!!!TODO NEED TO CHECK --------------- ### Internal * Using Core 14.0.1. From ef0075e1d94c3e2f1c8524ab9463dd860ae3b1ca Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 12:50:28 +0100 Subject: [PATCH 75/88] Updated guids to v24 --- .../Realm.Tests/EmbeddedResources/guids.realm | Bin 69632 -> 196608 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Tests/Realm.Tests/EmbeddedResources/guids.realm b/Tests/Realm.Tests/EmbeddedResources/guids.realm index cbe6aaf509b1b8431ae37205cb74b06567cc06a8..237805403b930c1b13e490268bca796ee4974cc9 100644 GIT binary patch delta 14206 zcmeH}ZETa*701tw^WuO>9B6_=+QgZ0NS4wjqm{zisivXD(mED2R)w*xmO!ngZOpW^ zL}F6R+sI&|NuNrNw2r1gRihRf3Y}8H2TwwpB}5l!$6K{7-D^HxX^V=X<5&|9S4c=iGD7{oi|?ov6%aLy^&LBiiN`c(`%p+D9WxMRrGCkC=tSYiKnh zRU#GpDwt*@aUc#tbXhK$3?0$kBDTEV1mw4i!3z$cwG`*+ekvB5u5O>byIrc3NP1S_~M|UpDZ)$pO3Bf{8vfnGh4H*hQRmkUb#@iDGZB*m$)g_ zK%p9_>3q6p1v6UqmS*hX4BX`}JB=n&kL5_;^!QuPKCh+3dmS`okr4Dq$k)L%=oRGh5w{?C3k4T2 zqP>cm^fzAZibo@5FG+N56R(PF01Ke!rE4PQJaAOE;;To!WFtp8tr72-9Vhybb1vUo zA8szQAkl1`izk!?|2Wa_ZVE25A%65SRMbp9ed|jVO#Ic!?uO8acIvTM2Z@5qj1xGu z@^+>2CmE!5E^{jteyY;>_Nj3ut*mujT~n={L#MmqQ+HTRm#m7XMf8T60ZVJX z<67x1IQ?h7;vR(P8Scuvy$U{X)~)T?-uU?4b^!6!&p1(Ek#+-jmJx$fO zyf?P7^B&KJxQO7t9~z%M)Y@ec5x6{H|6Sy}sAUKJYPk5)ms3lcc%14OY2?OItR?!3 z=i{+ps~1=JoCw!|jDT)j=%eVJtzK5=FSQckN0%PEy@}(Om_kdo-QL6}-j66b^c5*A z=Ep#LMJ4E#%kA}ZZC-Ng;6ylTobD@1sZCe)Kimq+`hRADRE?D-nXdQ@ zHvTgc?C|W1+W$AA7?RJz{C5GTzu(K?v*k8yo5i8*ZCFx z0sNeKI>wb2eofZOG)m?eXX!Wtuk&`jA(G;@>Uopr=q(KNzTouCy^RA!WRUYWi;Vw* zqO_UZLkrATjA{+GmTcc0Y#AzofWY~!~6jVB)X^! zGJss7={T?1!;u8sKnm-LWfO^RrZUL*HuMZ}aFa~&zhv||3c#};<^-pB$g?}aEP3p~ zb}!@mh>urq7Kt1n#={hjs!kq}Vd>*#2u@IdqYUIRIKfRz6^keNK7~`NnC8x$N3y5+ zen+HF?>la==)3e!VSk=LE^!xJrtgYK;wlSWV?GBm!(j}6g0Igg)GhoAbb#b3NZV6A zOdV>B48gNvw^u36JSjws%yT&_`Z9u^MpC7|B&afo3?M^D2A5e-`xA)vXOI*!hxq+2 z4%3lFd}GBt(t{+BzR#gQSI0<1qKMAdZ(ZC+^`m+QDKbaLMv+M*i6jv1duF17MLzU0 z50LrO|EpDd+<-F+BN$J@EhE)31aC55gqu`w5&im)Bt;+oG)Co=)BFBXr{}NtTCp{- z9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Iza) m9Iza)9Iza)9Iza)9Iza)9Iza)9Iza)9Izbte>gCfX#Ed~5)0P= delta 166 zcmZo@;AvREBJm#zK5#KIfIx_@i<7uG17oAaQVB+G0hsU!wWsDmLM8^272jA-Hh7~$ HC#(tpl_W); From 26bfc485a67a58dab872f98772877eed96d670e6 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:18:44 +0100 Subject: [PATCH 76/88] Corrected methods --- Tests/Realm.Tests/Sync/SyncTestBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Realm.Tests/Sync/SyncTestBase.cs b/Tests/Realm.Tests/Sync/SyncTestBase.cs index a7826a2da8..57e862fcdb 100644 --- a/Tests/Realm.Tests/Sync/SyncTestBase.cs +++ b/Tests/Realm.Tests/Sync/SyncTestBase.cs @@ -275,7 +275,7 @@ protected PartitionSyncConfiguration GetFakeConfig(App? app = null, string? user if (setFakeSyncRoute) { - SetFakeSyncRoute(app); + SetFakeSyncRoute(user.App); } return UpdateConfig(new PartitionSyncConfiguration(Guid.NewGuid().ToString(), user, optionalPath)); @@ -288,7 +288,7 @@ protected FlexibleSyncConfiguration GetFakeFLXConfig(App? app = null, string? us if (setFakeSyncRoute) { - SetFakeSyncRoute(app); + SetFakeSyncRoute(user.App); } return UpdateConfig(new FlexibleSyncConfiguration(user, optionalPath)); From 28b8b886f3573ac78ad1828c7d2a008edd699ade Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:00:44 +0100 Subject: [PATCH 77/88] Corrected changelog and wrapper --- CHANGELOG.md | 1 + wrappers/src/app_cs.cpp | 20 ++++++-------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f05a0021..6af312891a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * `String` and `byte[]` are now strongly typed for comparisons and queries. This change is especially relevant when querying for a string constant on a `RealmValue` property, as now only strings will be returned. If searching for binary data is desired, then that type must be specified by the constant. In RQL (`.Filter()`) the new way to specify a binary constant is to use `RealmValueProp = bin('xyz')` or `RealmValueProp = binary('xyz')`. (Core 14.0.0-beta.0) * Sorting order of strings has changed to use standard unicode codepoint order instead of grouping similar english letters together. A noticeable change will be from "aAbBzZ" to "ABZabz". (Core 14.0.0-beta.0) * In RQL (`Filter()`), ff you want to query using `@type` operation, you must use `objectlink` to match links to objects. `object` is reserved for dictionary types. (Core 14.0.0) +* Opening file with file format 23 (Realm .NET versions ealrier than 12.0.0) in read-only mode will crash. (Core 14.0.0) ### Enhancements * Add support for passing a key paths collection (`KeyPathsCollection`) when using `IRealmCollection.SubscribeForNotifications`. Passing a `KeyPathsCollection` allows to specify which changes in properties should raise a notification. diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index f95dd221e3..9820041e24 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -296,26 +296,18 @@ extern "C" { } REALM_EXPORT void shared_app_reset_for_testing(SharedApp& app) { + + if (!app->sync_manager()->get_logger()) { + return; + } + auto users = app->all_users(); for (size_t i = 0; i < users.size(); i++) { auto &user = users[i]; user->log_out(); } - while (app->sync_manager()->has_existing_sessions()) { - sleep_ms(5); - } - - bool did_reset = false; - while (!did_reset) { - try { - app->sync_manager()->tear_down_for_testing(); - did_reset = true; - } - catch (...) { - - } - } + app->sync_manager()->tear_down_for_testing(); App::clear_cached_apps(); } From 2479b2aacb1248fb70c3be6344884bf53b8ea35b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:12:01 +0100 Subject: [PATCH 78/88] Added comments --- wrappers/src/app_cs.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrappers/src/app_cs.cpp b/wrappers/src/app_cs.cpp index 9820041e24..936ca73aa4 100644 --- a/wrappers/src/app_cs.cpp +++ b/wrappers/src/app_cs.cpp @@ -297,6 +297,7 @@ extern "C" { REALM_EXPORT void shared_app_reset_for_testing(SharedApp& app) { + // If the logger is empty then tear_down_for_testing has been called already if (!app->sync_manager()->get_logger()) { return; } @@ -307,6 +308,7 @@ extern "C" { user->log_out(); } + // This method will crash the application if called more than once for the same app. app->sync_manager()->tear_down_for_testing(); App::clear_cached_apps(); From 2a8e89eff9fc19f79c9c5e1eec3d46c8cd5fbf0b Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:21:57 +0100 Subject: [PATCH 79/88] Corrected changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6af312891a..372d5d4ff4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,7 +35,6 @@ * Improved performance of RQL (`.Filter()`) queries on a non-linked string property using: >, >=, <, <=, operators and fixed behaviour that a null string should be evaulated as less than everything, previously nulls were not matched. (Core 13.27.0) * Updated bundled OpenSSL version to 3.2.0. (Core 13.27.0) * Storage of Decimal128 properties has been optimised so that the individual values will take up 0 bits (if all nulls), 32 bits, 64 bits or 128 bits depending on what is needed. (Core 14.0.0-beta.0) -* Querying a specific entry in a collection (in particular 'first and 'last') is supported in RQL (`.Filter()`). (Core 14.0.0-beta.0) * Add support for collection indexes in RQL (`Filter()`) queries. For example: ```csharp @@ -67,7 +66,7 @@ * Fix a minor race condition when backing up Realm files before a client reset which could have lead to overwriting an existing file. (Core 13.27.0) * Boolean property `ChangeSet.IsCleared` that is true when the collection gets cleared is now also raised for `IDictionary`, aligning it to `ISet` and `IList`. (Core 14.0.0-beta.0) * Fixed equality queries on `RealmValue` properties with an index. (Core 14.0.0-beta.0) -* Fixed a crash that would happen when more than 8388606 were pointing to a specific object. +* Fixed a crash that would happen when more than 8388606 links were pointing to a specific object. * Fixed wrong results when querying for `NULL` value in `IDictionary`. (Core 14.0.0-beta.0) * A Realm generated on a non-apple ARM 64 device and copied to another platform (and vice-versa) were non-portable due to a sorting order difference. This impacts strings or binaries that have their first difference at a non-ascii character. These items may not be found in a set, or in an indexed column if the strings had a long common prefix (> 200 characters). (Core 14.0.0-beta.0) From bf98fd4e9687c548965ecce4e1a9f67316618e32 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 28 Feb 2024 15:25:13 +0100 Subject: [PATCH 80/88] Corrected changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 372d5d4ff4..c95a35f776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,8 @@ * Support for upgrading from Realm files produced by RealmCore v5.23.9 (Realm .NET v5.0.1) or earlier is no longer supported. (Core 14.0.0-beta.0) * `String` and `byte[]` are now strongly typed for comparisons and queries. This change is especially relevant when querying for a string constant on a `RealmValue` property, as now only strings will be returned. If searching for binary data is desired, then that type must be specified by the constant. In RQL (`.Filter()`) the new way to specify a binary constant is to use `RealmValueProp = bin('xyz')` or `RealmValueProp = binary('xyz')`. (Core 14.0.0-beta.0) * Sorting order of strings has changed to use standard unicode codepoint order instead of grouping similar english letters together. A noticeable change will be from "aAbBzZ" to "ABZabz". (Core 14.0.0-beta.0) -* In RQL (`Filter()`), ff you want to query using `@type` operation, you must use `objectlink` to match links to objects. `object` is reserved for dictionary types. (Core 14.0.0) -* Opening file with file format 23 (Realm .NET versions ealrier than 12.0.0) in read-only mode will crash. (Core 14.0.0) +* In RQL (`Filter()`), if you want to query using `@type` operation, you must use `objectlink` to match links to objects. `object` is reserved for dictionary types. (Core 14.0.0) +* Opening realm with file format 23 or lower (Realm .NET versions earlier than 12.0.0) in read-only mode will crash. (Core 14.0.0) ### Enhancements * Add support for passing a key paths collection (`KeyPathsCollection`) when using `IRealmCollection.SubscribeForNotifications`. Passing a `KeyPathsCollection` allows to specify which changes in properties should raise a notification. From 2d04e6182255e3195f0e83a8f18ad6cbb88c5b7f Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:26:22 +0100 Subject: [PATCH 81/88] Updated core --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 2bfd5e9ba7..e1042b8504 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 2bfd5e9ba74ed2620657bf968d2931f6ceab6785 +Subproject commit e1042b8504e277820f14cb884bd6ee5578ef8cb6 From b5d6e1d0bd7f796362825b8e6d4a600a34172519 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:29:12 +0100 Subject: [PATCH 82/88] Updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c95a35f776..7ab7432b8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,7 +75,7 @@ * Realm Studio: 15.0.0 or later !!!!!!!!TODO NEED TO CHECK --------------- ### Internal -* Using Core 14.0.1. +* Using Core 14.1.0. ## 11.7.0 (2024-02-05) From b41a033410c623c2b95ff45ffb03babb30432dfc Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Mar 2024 12:48:48 +0100 Subject: [PATCH 83/88] Used correct core version --- wrappers/realm-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/realm-core b/wrappers/realm-core index 7083d1018f..e1042b8504 160000 --- a/wrappers/realm-core +++ b/wrappers/realm-core @@ -1 +1 @@ -Subproject commit 7083d1018fda1d968b7dc9a3f8471e843f1c85e5 +Subproject commit e1042b8504e277820f14cb884bd6ee5578ef8cb6 From 8149c89f0ea0f6b6b477bd3fdb739fe9d53e73a2 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:13:23 +0100 Subject: [PATCH 84/88] Fixed changelog --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 436cb6c163..f56852c566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,10 +85,6 @@ * Fixed wrong results when querying for `NULL` value in `IDictionary`. (Core 14.0.0-beta.0) * A Realm generated on a non-apple ARM 64 device and copied to another platform (and vice-versa) were non-portable due to a sorting order difference. This impacts strings or binaries that have their first difference at a non-ascii character. These items may not be found in a set, or in an indexed column if the strings had a long common prefix (> 200 characters). (Core 14.0.0-beta.0) - -### Compatibility -* Realm Studio: 15.0.0 or later !!!!!!!!TODO NEED TO CHECK --------------- - ### Compatibility * Realm Studio: 15.0.0 or later. From bfd027fcf5ab7ea34dcdfee8b6359fc8acc0b3ad Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:32:17 +0100 Subject: [PATCH 85/88] Fixed changelog --- CHANGELOG.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f56852c566..bd04a796b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,6 @@ var retrievedList = rvo.RealmValueProperty.AsList(); ``` (PR [#3441](https://github.com/realm/realm-dotnet/pull/3441)) - * Reduced memory usage of `RealmValue`. (PR [#3441](https://github.com/realm/realm-dotnet/pull/3441)) * Add support for passing a key paths collection (`KeyPathsCollection`) when using `IRealmCollection.SubscribeForNotifications`. Passing a `KeyPathsCollection` allows to specify which changes in properties should raise a notification. @@ -153,10 +152,6 @@ * Made WebSocket error logging more verbose when using `AppConfiguration.UseManagedWebSockets = true`. [#3459](https://github.com/realm/realm-dotnet/pull/3459) ### Fixed -* Fixed serveral smaller issues when logging into with a single user when using multiple auth providers. (Core 13.21.0) -* Fixed an excepton when querying over a geospatial dataset that some objects with a type property set to something other than `Point` (case insensitive). Those objects are now just ignored. (Core 13.21.0) -* Fixed an issue that would make the user in an inconstisten state if the user was logged out while an access toekn refresh was in progress. (Core 13.21.0) -* Fixed an issue where Android.bp builds would fail with SSL certificat validation errors because the trusted CA roots bundle was not included. (Core 13.23.0) * Added an error that is raised when interface based Realm classes are used with a language version lower than 8.0. At the same time, removed the use of `not` in the generated code, so that it's compatible with a minumum C# version of 8.0. (Issue [#3265](https://github.com/realm/realm-dotnet/issues/3265)) * Logging into a single user using multiple auth providers created a separate SyncUser per auth provider. This mostly worked, but had some quirks: - Sync sessions would not necessarily be associated with the specific SyncUser used to create them. As a result, querying a user for its sessions could give incorrect results, and logging one user out could close the wrong sessions. @@ -175,7 +170,6 @@ * Realm Studio: 13.0.0 or later. ### Internal -* Using Core 13.23.0 * Using Core 13.23.1. ## 11.5.0 (2023-09-15) From 69d0610426249b77e7853d31730fa71614391291 Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Wed, 6 Mar 2024 13:57:15 +0100 Subject: [PATCH 86/88] Added missing tests --- .../Database/RealmValueWithCollections.cs | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index e1b08eafef..81bb049531 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -396,7 +396,7 @@ public void List_WhenManaged_WorksWithDynamic() [Test] public void List_WhenManaged_WorksWithNotifications() { - var originalList = ListGenerator(1); + var originalList = new List { 1, ListGenerator(1), DictGenerator(1) }; var rvo = _realm.Write(() => { @@ -414,12 +414,36 @@ public void List_WhenManaged_WorksWithNotifications() _realm.Write(() => { - rvo.RealmValueProperty.AsList()[1] = "mario"; + rvo.RealmValueProperty.AsList()[0] = "mario"; }); _realm.Refresh(); + Assert.That(callbacks.Count, Is.EqualTo(1)); + Assert.That(callbacks[0].ModifiedIndices, Is.EqualTo(new[] { 0 })); + + callbacks.Clear(); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[1].AsList()[0] = "luigi"; + }); + + _realm.Refresh(); + Assert.That(callbacks.Count, Is.EqualTo(1)); + Assert.That(callbacks[0].ModifiedIndices, Is.EqualTo(new[] { 1 })); + + callbacks.Clear(); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsList()[2].AsDictionary()["s1"] = "peach"; + }); + _realm.Refresh(); Assert.That(callbacks.Count, Is.EqualTo(1)); + Assert.That(callbacks[0].ModifiedIndices, Is.EqualTo(new[] { 2 })); + + callbacks.Clear(); } #endregion @@ -755,7 +779,7 @@ public void Dictionary_WhenManaged_WorksWithDynamic() [Test] public void Dictionary_WhenManaged_WorksWithNotifications() { - var dictVal = DictGenerator(1); + var dictVal = new Dictionary { { "s1", 1 }, { "s2", ListGenerator(1) }, { "s3", DictGenerator(1) } }; var rvo = _realm.Write(() => { @@ -773,12 +797,95 @@ public void Dictionary_WhenManaged_WorksWithNotifications() _realm.Write(() => { - rvo.RealmValueProperty.AsDictionary()["s2"] = "mario"; + rvo.RealmValueProperty.AsDictionary()["s1"] = "mario"; }); _realm.Refresh(); + Assert.That(callbacks.Count, Is.EqualTo(1)); + + callbacks.Clear(); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["s2"].AsList()[0] = "mario"; + }); + + _realm.Refresh(); + Assert.That(callbacks.Count, Is.EqualTo(1)); + + callbacks.Clear(); + + _realm.Write(() => + { + rvo.RealmValueProperty.AsDictionary()["s3"].AsDictionary()["s1"] = "peach"; + }); + _realm.Refresh(); Assert.That(callbacks.Count, Is.EqualTo(1)); + + callbacks.Clear(); + } + + [Test] + public void MixedCollection_Filter_CountSizeType() + { + var ob1 = new RealmValueObject { RealmValueProperty = 2 }; + var ob2 = new RealmValueObject { RealmValueProperty = new List { 1, "string", 23.0 } }; + var ob3 = new RealmValueObject { RealmValueProperty = new List { 1, "string" } }; + var ob4 = new RealmValueObject { RealmValueProperty = new Dictionary { { "s1", 22 } } }; + + _realm.Write(() => + { + _realm.Add(ob1); + _realm.Add(ob2); + _realm.Add(ob3); + _realm.Add(ob4); + }); + + var rvos = _realm.All(); + + var q = rvos.Filter("RealmValueProperty.@size <= 2"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob3.Id, ob4.Id })); + + q = rvos.Filter("RealmValueProperty.@count > 2"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id })); + + q = rvos.Filter("RealmValueProperty.@type == 'dictionary'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob4.Id })); + + q = rvos.Filter("RealmValueProperty.@type == 'list'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob3.Id })); + + q = rvos.Filter("RealmValueProperty.@type == 'collection'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob3.Id, ob4.Id })); + } + + [Test] + public void MixedCollection_Filter_AnyAllNone() + { + var ob1 = new RealmValueObject { RealmValueProperty = 2 }; + var ob2 = new RealmValueObject { RealmValueProperty = new List { "a", "string" } }; + var ob3 = new RealmValueObject { RealmValueProperty = new List { 1, "string" } }; + var ob4 = new RealmValueObject { RealmValueProperty = new List { 1, 23 } }; + + _realm.Write(() => + { + _realm.Add(ob1); + _realm.Add(ob2); + _realm.Add(ob3); + _realm.Add(ob4); + }); + + var rvos = _realm.All(); + + var q = rvos.Filter("ANY RealmValueProperty[*].@type == 'string'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob3.Id })); + + q = rvos.Filter("NONE RealmValueProperty[*].@type == 'string'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob1.Id, ob4.Id })); + + q = rvos.Filter("ALL RealmValueProperty[*].@type == 'string'"); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id })); } #endregion From 7a8de04474e6df7ab19a975ad02d5e10a8813d0d Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:55:54 +0100 Subject: [PATCH 87/88] Fixed tests --- Tests/Realm.Tests/Database/RealmValueWithCollections.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index 81bb049531..e186ab077f 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -881,11 +881,12 @@ public void MixedCollection_Filter_AnyAllNone() var q = rvos.Filter("ANY RealmValueProperty[*].@type == 'string'"); Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob3.Id })); + // NONE and ALL match both also on "empty lists", that's why they match also on ob1 q = rvos.Filter("NONE RealmValueProperty[*].@type == 'string'"); Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob1.Id, ob4.Id })); q = rvos.Filter("ALL RealmValueProperty[*].@type == 'string'"); - Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id })); + Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob1.Id })); } #endregion From 0d40c413d7b95d5c5bbd617433238d209c5ddbed Mon Sep 17 00:00:00 2001 From: Ferdinando Papale <4850119+papafe@users.noreply.github.com> Date: Thu, 7 Mar 2024 14:06:52 +0100 Subject: [PATCH 88/88] [RNET-1092] Added support for indexed RealmValue (#3544) * Added support for indexed RealmValue * Fixed changelog * Corrected indexable property types in schema * Added missing newline in the changelog * Testing fix for CI --- CHANGELOG.md | 1 + Realm/Realm.SourceGenerator/InfoClasses.cs | 1 + .../PropertyDefinitionExtensions.cs | 1 + Realm/Realm/Schema/Property.cs | 1 + .../Database/RealmValueWithCollections.cs | 68 +++ Tests/Realm.Tests/Database/TestObjects.cs | 10 + .../IndexedRealmValueObject_generated.cs | 412 ++++++++++++++++++ .../ErrorClasses/UnsupportedIndexableTypes.cs | 6 - .../IndexedClass_generated.cs | 58 +++ .../UnsupportedIndexableTypes.diagnostics.cs | 56 +-- .../TestClasses/IndexedClass.cs | 6 + .../RootRealmClass_generated.cs | 276 +----------- .../Weaver/AssemblyToProcess/FaultyClasses.cs | 5 +- 13 files changed, 584 insertions(+), 317 deletions(-) create mode 100644 Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedRealmValueObject_generated.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a62d23a04..09565451e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ var query5 = people.Filter("ListOfDogs[SIZE] = $0", 3) ``` (Core 14.0.0) +* Added support for indexed `RealmValue` properties. (PR [#3544](https://github.com/realm/realm-dotnet/pull/3544)) ### Fixed * Fixed RQL (`.Filter()`) queries like `indexed_property == NONE {x}` which mistakenly matched on only x instead of not x. This only applies when an indexed property with equality (==, or IN) matches with `NONE` on a list of one item. If the constant list contained more than one value then it was working correctly. (Core 13.27.0) diff --git a/Realm/Realm.SourceGenerator/InfoClasses.cs b/Realm/Realm.SourceGenerator/InfoClasses.cs index 2a15ae8ae7..c852de97d2 100644 --- a/Realm/Realm.SourceGenerator/InfoClasses.cs +++ b/Realm/Realm.SourceGenerator/InfoClasses.cs @@ -118,6 +118,7 @@ internal abstract record PropertyTypeInfo ScalarType.ObjectId, ScalarType.Guid, ScalarType.Date, + ScalarType.RealmValue, }; private static readonly HashSet _primaryKeyTypes = new() diff --git a/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs b/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs index fcdcd23d9b..d467a30f52 100644 --- a/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs +++ b/Realm/Realm.Weaver/Extensions/PropertyDefinitionExtensions.cs @@ -41,6 +41,7 @@ internal static class PropertyDefinitionExtensions DateTimeOffsetTypeName, ObjectIdTypeName, GuidTypeName, + RealmValueTypeName, }; internal static bool IsAutomatic(this PropertyDefinition property) diff --git a/Realm/Realm/Schema/Property.cs b/Realm/Realm/Schema/Property.cs index c4b6de3955..b2324b4ca4 100644 --- a/Realm/Realm/Schema/Property.cs +++ b/Realm/Realm/Schema/Property.cs @@ -41,6 +41,7 @@ public readonly struct Property PropertyType.ObjectId, PropertyType.Guid, PropertyType.Date, + PropertyType.RealmValue }; internal static readonly HashSet PrimaryKeyTypes = new() diff --git a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs index e186ab077f..5e76b6fdac 100644 --- a/Tests/Realm.Tests/Database/RealmValueWithCollections.cs +++ b/Tests/Realm.Tests/Database/RealmValueWithCollections.cs @@ -889,6 +889,74 @@ public void MixedCollection_Filter_AnyAllNone() Assert.That(q.ToList().Select(i => i.Id), Is.EquivalentTo(new[] { ob2.Id, ob1.Id })); } + [Test] + public void IndexedRealmValue_WithCollection_BasicTest() + { + var innerList = ListGenerator(1); + var innerDict = DictGenerator(1); + + var originalList = new List + { + RealmValue.Null, + 1, + true, + "string", + new byte[] { 0, 1, 2 }, + new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero), + 1f, + 2d, + 3m, + new ObjectId("5f63e882536de46d71877979"), + Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31"), + new InternalObject { IntProperty = 10, StringProperty = "brown" }, + innerList, + innerDict, + }; + + var obj = _realm.Write(() => + { + return _realm.Add(new IndexedRealmValueObject { RealmValueProperty = originalList }); + }); + + RealmValue rv = obj.RealmValueProperty; + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.List)); + Assert.That(rv != RealmValue.Null); + Assert.That(rv.AsList(), Is.EqualTo(originalList)); + + var originalDict = new Dictionary + { + { "1", RealmValue.Null }, + { "2", 1 }, + { "3", true }, + { "4", "string" }, + { "5", new byte[] { 0, 1, 2 } }, + { "6", new DateTimeOffset(1234, 5, 6, 7, 8, 9, TimeSpan.Zero) }, + { "7", 1f }, + { "8", 2d }, + { "9", 3m }, + { "a", new ObjectId("5f63e882536de46d71877979") }, + { "b", Guid.Parse("3809d6d9-7618-4b3d-8044-2aa35fd02f31") }, + { "c", new InternalObject { IntProperty = 10, StringProperty = "brown" } }, + { "d", innerList }, + { "e", innerDict }, + }; + + _realm.Write(() => + { + obj.RealmValueProperty = originalDict; + }); + + rv = obj.RealmValueProperty; + + Assert.That(rv.Type, Is.EqualTo(RealmValueType.Dictionary)); + Assert.That(rv != RealmValue.Null, "Different than null"); + + Assert.That(rv.AsDictionary(), Is.EquivalentTo(originalDict)); + Assert.That(rv.AsAny(), Is.EquivalentTo(originalDict)); + Assert.That(rv.As>(), Is.EquivalentTo(originalDict)); + } + #endregion } } diff --git a/Tests/Realm.Tests/Database/TestObjects.cs b/Tests/Realm.Tests/Database/TestObjects.cs index dd77bdbee7..ddfee7695a 100644 --- a/Tests/Realm.Tests/Database/TestObjects.cs +++ b/Tests/Realm.Tests/Database/TestObjects.cs @@ -1127,6 +1127,16 @@ public partial class RealmValueObject : TestRealmObject public IDictionary TestDict { get; } = null!; } + public partial class IndexedRealmValueObject : TestRealmObject + { + [PrimaryKey] + [MapTo("_id")] + public int Id { get; set; } = TestHelpers.Random.Next(); + + [Indexed] + public RealmValue RealmValueProperty { get; set; } + } + public partial class LinksObject : TestRealmObject { [PrimaryKey] diff --git a/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedRealmValueObject_generated.cs b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedRealmValueObject_generated.cs new file mode 100644 index 0000000000..01d1413ac1 --- /dev/null +++ b/Tests/Realm.Tests/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedRealmValueObject_generated.cs @@ -0,0 +1,412 @@ +// +#nullable enable + +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using Realms; +using Realms.Schema; +using Realms.Tests; +using Realms.Tests.Database; +using Realms.Weaving; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Xml.Serialization; +using TestEmbeddedObject = Realms.IEmbeddedObject; +using TestRealmObject = Realms.IRealmObject; + +namespace Realms.Tests +{ + [Generated] + [Woven(typeof(IndexedRealmValueObjectObjectHelper)), Realms.Preserve(AllMembers = true)] + public partial class IndexedRealmValueObject : IRealmObject, INotifyPropertyChanged, IReflectableType + { + + [Realms.Preserve] + static IndexedRealmValueObject() + { + Realms.Serialization.RealmObjectSerializer.Register(new IndexedRealmValueObjectSerializer()); + } + + /// + /// Defines the schema for the class. + /// + public static Realms.Schema.ObjectSchema RealmSchema = new Realms.Schema.ObjectSchema.Builder("IndexedRealmValueObject", ObjectSchema.ObjectType.RealmObject) + { + Realms.Schema.Property.Primitive("_id", Realms.RealmValueType.Int, isPrimaryKey: true, indexType: IndexType.None, isNullable: false, managedName: "Id"), + Realms.Schema.Property.RealmValue("RealmValueProperty", managedName: "RealmValueProperty"), + }.Build(); + + #region IRealmObject implementation + + private IIndexedRealmValueObjectAccessor? _accessor; + + Realms.IRealmAccessor Realms.IRealmObjectBase.Accessor => Accessor; + + private IIndexedRealmValueObjectAccessor Accessor => _accessor ??= new IndexedRealmValueObjectUnmanagedAccessor(typeof(IndexedRealmValueObject)); + + /// + [IgnoreDataMember, XmlIgnore] + public bool IsManaged => Accessor.IsManaged; + + /// + [IgnoreDataMember, XmlIgnore] + public bool IsValid => Accessor.IsValid; + + /// + [IgnoreDataMember, XmlIgnore] + public bool IsFrozen => Accessor.IsFrozen; + + /// + [IgnoreDataMember, XmlIgnore] + public Realms.Realm? Realm => Accessor.Realm; + + /// + [IgnoreDataMember, XmlIgnore] + public Realms.Schema.ObjectSchema ObjectSchema => Accessor.ObjectSchema!; + + /// + [IgnoreDataMember, XmlIgnore] + public Realms.DynamicObjectApi DynamicApi => Accessor.DynamicApi; + + /// + [IgnoreDataMember, XmlIgnore] + public int BacklinksCount => Accessor.BacklinksCount; + + void ISettableManagedAccessor.SetManagedAccessor(Realms.IRealmAccessor managedAccessor, Realms.Weaving.IRealmObjectHelper? helper, bool update, bool skipDefaults) + { + var newAccessor = (IIndexedRealmValueObjectAccessor)managedAccessor; + var oldAccessor = _accessor; + _accessor = newAccessor; + + if (helper != null && oldAccessor != null) + { + if (!skipDefaults || oldAccessor.Id != default(int)) + { + newAccessor.Id = oldAccessor.Id; + } + newAccessor.RealmValueProperty = oldAccessor.RealmValueProperty; + } + + if (_propertyChanged != null) + { + SubscribeForNotifications(); + } + + OnManaged(); + } + + #endregion + + /// + /// Called when the object has been managed by a Realm. + /// + /// + /// This method will be called either when a managed object is materialized or when an unmanaged object has been + /// added to the Realm. It can be useful for providing some initialization logic as when the constructor is invoked, + /// it is not yet clear whether the object is managed or not. + /// + partial void OnManaged(); + + private event PropertyChangedEventHandler? _propertyChanged; + + /// + public event PropertyChangedEventHandler? PropertyChanged + { + add + { + if (_propertyChanged == null) + { + SubscribeForNotifications(); + } + + _propertyChanged += value; + } + + remove + { + _propertyChanged -= value; + + if (_propertyChanged == null) + { + UnsubscribeFromNotifications(); + } + } + } + + /// + /// Called when a property has changed on this class. + /// + /// The name of the property. + /// + /// For this method to be called, you need to have first subscribed to . + /// This can be used to react to changes to the current object, e.g. raising for computed properties. + /// + /// + /// + /// class MyClass : IRealmObject + /// { + /// public int StatusCodeRaw { get; set; } + /// public StatusCodeEnum StatusCode => (StatusCodeEnum)StatusCodeRaw; + /// partial void OnPropertyChanged(string propertyName) + /// { + /// if (propertyName == nameof(StatusCodeRaw)) + /// { + /// RaisePropertyChanged(nameof(StatusCode)); + /// } + /// } + /// } + /// + /// Here, we have a computed property that depends on a persisted one. In order to notify any + /// subscribers that StatusCode has changed, we implement and + /// raise manually by calling . + /// + partial void OnPropertyChanged(string? propertyName); + + private void RaisePropertyChanged([CallerMemberName] string propertyName = "") + { + _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + OnPropertyChanged(propertyName); + } + + private void SubscribeForNotifications() + { + Accessor.SubscribeForNotifications(RaisePropertyChanged); + } + + private void UnsubscribeFromNotifications() + { + Accessor.UnsubscribeFromNotifications(); + } + + /// + /// Converts a to . Equivalent to . + /// + /// The to convert. + /// The stored in the . + public static explicit operator IndexedRealmValueObject?(Realms.RealmValue val) => val.Type == Realms.RealmValueType.Null ? null : val.AsRealmObject(); + + /// + /// Implicitly constructs a from . + /// + /// The value to store in the . + /// A containing the supplied . + public static implicit operator Realms.RealmValue(IndexedRealmValueObject? val) => val == null ? Realms.RealmValue.Null : Realms.RealmValue.Object(val); + + /// + /// Implicitly constructs a from . + /// + /// The value to store in the . + /// A containing the supplied . + public static implicit operator Realms.QueryArgument(IndexedRealmValueObject? val) => (Realms.RealmValue)val; + + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public TypeInfo GetTypeInfo() => Accessor.GetTypeInfo(this); + + /// + public override bool Equals(object? obj) + { + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj is InvalidObject) + { + return !IsValid; + } + + if (!(obj is Realms.IRealmObjectBase iro)) + { + return false; + } + + return Accessor.Equals(iro.Accessor); + } + + /// + public override int GetHashCode() => IsManaged ? Accessor.GetHashCode() : base.GetHashCode(); + + /// + public override string? ToString() => Accessor.ToString(); + + [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] + private class IndexedRealmValueObjectObjectHelper : Realms.Weaving.IRealmObjectHelper + { + public void CopyToRealm(Realms.IRealmObjectBase instance, bool update, bool skipDefaults) + { + throw new InvalidOperationException("This method should not be called for source generated classes."); + } + + public Realms.ManagedAccessor CreateAccessor() => new IndexedRealmValueObjectManagedAccessor(); + + public Realms.IRealmObjectBase CreateInstance() => new IndexedRealmValueObject(); + + public bool TryGetPrimaryKeyValue(Realms.IRealmObjectBase instance, out RealmValue value) + { + value = ((IIndexedRealmValueObjectAccessor)instance.Accessor).Id; + return true; + } + } + + [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] + internal interface IIndexedRealmValueObjectAccessor : Realms.IRealmAccessor + { + int Id { get; set; } + + Realms.RealmValue RealmValueProperty { get; set; } + } + + [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] + private class IndexedRealmValueObjectManagedAccessor : Realms.ManagedAccessor, IIndexedRealmValueObjectAccessor + { + public int Id + { + get => (int)GetValue("_id"); + set => SetValueUnique("_id", value); + } + + public Realms.RealmValue RealmValueProperty + { + get => (Realms.RealmValue)GetValue("RealmValueProperty"); + set => SetValue("RealmValueProperty", value); + } + } + + [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] + private class IndexedRealmValueObjectUnmanagedAccessor : Realms.UnmanagedAccessor, IIndexedRealmValueObjectAccessor + { + public override ObjectSchema ObjectSchema => IndexedRealmValueObject.RealmSchema; + + private int _id = TestHelpers.Random.Next(); + public int Id + { + get => _id; + set + { + _id = value; + RaisePropertyChanged("Id"); + } + } + + private Realms.RealmValue _realmValueProperty; + public Realms.RealmValue RealmValueProperty + { + get => _realmValueProperty; + set + { + _realmValueProperty = value; + RaisePropertyChanged("RealmValueProperty"); + } + } + + public IndexedRealmValueObjectUnmanagedAccessor(Type objectType) : base(objectType) + { + } + + public override Realms.RealmValue GetValue(string propertyName) + { + return propertyName switch + { + "_id" => _id, + "RealmValueProperty" => _realmValueProperty, + _ => throw new MissingMemberException($"The object does not have a gettable Realm property with name {propertyName}"), + }; + } + + public override void SetValue(string propertyName, Realms.RealmValue val) + { + switch (propertyName) + { + case "_id": + throw new InvalidOperationException("Cannot set the value of a primary key property with SetValue. You need to use SetValueUnique"); + case "RealmValueProperty": + RealmValueProperty = (Realms.RealmValue)val; + return; + default: + throw new MissingMemberException($"The object does not have a settable Realm property with name {propertyName}"); + } + } + + public override void SetValueUnique(string propertyName, Realms.RealmValue val) + { + if (propertyName != "_id") + { + throw new InvalidOperationException($"Cannot set the value of non primary key property ({propertyName}) with SetValueUnique"); + } + + Id = (int)val; + } + + public override IList GetListValue(string propertyName) + { + throw new MissingMemberException($"The object does not have a Realm list property with name {propertyName}"); + } + + public override ISet GetSetValue(string propertyName) + { + throw new MissingMemberException($"The object does not have a Realm set property with name {propertyName}"); + } + + public override IDictionary GetDictionaryValue(string propertyName) + { + throw new MissingMemberException($"The object does not have a Realm dictionary property with name {propertyName}"); + } + } + + [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] + private class IndexedRealmValueObjectSerializer : Realms.Serialization.RealmObjectSerializerBase + { + public override string SchemaName => "IndexedRealmValueObject"; + + protected override void SerializeValue(MongoDB.Bson.Serialization.BsonSerializationContext context, BsonSerializationArgs args, IndexedRealmValueObject value) + { + context.Writer.WriteStartDocument(); + + WriteValue(context, args, "_id", value.Id); + WriteValue(context, args, "RealmValueProperty", value.RealmValueProperty); + + context.Writer.WriteEndDocument(); + } + + protected override IndexedRealmValueObject CreateInstance() => new IndexedRealmValueObject(); + + protected override void ReadValue(IndexedRealmValueObject instance, string name, BsonDeserializationContext context) + { + switch (name) + { + case "_id": + instance.Id = BsonSerializer.LookupSerializer().Deserialize(context); + break; + case "RealmValueProperty": + instance.RealmValueProperty = BsonSerializer.LookupSerializer().Deserialize(context); + break; + default: + context.Reader.SkipValue(); + break; + } + } + + protected override void ReadArrayElement(IndexedRealmValueObject instance, string name, BsonDeserializationContext context) + { + // No persisted list/set properties to deserialize + } + + protected override void ReadDocumentField(IndexedRealmValueObject instance, string name, string fieldName, BsonDeserializationContext context) + { + // No persisted dictionary properties to deserialize + } + } + } +} diff --git a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/ErrorClasses/UnsupportedIndexableTypes.cs b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/ErrorClasses/UnsupportedIndexableTypes.cs index 69138d23a4..f070badd1f 100644 --- a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/ErrorClasses/UnsupportedIndexableTypes.cs +++ b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/ErrorClasses/UnsupportedIndexableTypes.cs @@ -36,9 +36,6 @@ public partial class UnsupportedIndexableTypes : IRealmObject [Indexed] public RealmObj? ObjectProp { get; set; } - [Indexed] - public RealmValue RealmvalueProp { get; set; } - [Indexed] public decimal DecimalProp { get; set; } @@ -63,9 +60,6 @@ public partial class UnsupportedIndexableTypes : IRealmObject [Indexed(IndexType.None)] public int NoneIndexedInt { get; set; } - [Indexed(IndexType.General)] - public RealmValue GeneralRealmValueProp { get; set; } - [PrimaryKey] [Indexed] public int IndexedPrimaryKeyProp { get; set; } diff --git a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedClass_generated.cs b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedClass_generated.cs index 0d3414023f..b3b103a98b 100644 --- a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedClass_generated.cs +++ b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/IndexedClass_generated.cs @@ -39,6 +39,8 @@ static IndexedClass() Realms.Schema.Property.Primitive("IntProp", Realms.RealmValueType.Int, isPrimaryKey: false, indexType: IndexType.General, isNullable: false, managedName: "IntProp"), Realms.Schema.Property.Primitive("GuidProp", Realms.RealmValueType.Guid, isPrimaryKey: false, indexType: IndexType.General, isNullable: false, managedName: "GuidProp"), Realms.Schema.Property.Primitive("GeneralGuidProp", Realms.RealmValueType.Guid, isPrimaryKey: false, indexType: IndexType.General, isNullable: false, managedName: "GeneralGuidProp"), + Realms.Schema.Property.RealmValue("RealmValueProp", managedName: "RealmValueProp"), + Realms.Schema.Property.RealmValue("GeneralRealmValueProp", managedName: "GeneralRealmValueProp"), }.Build(); #region IRealmObject implementation @@ -106,6 +108,8 @@ void ISettableManagedAccessor.SetManagedAccessor(Realms.IRealmAccessor managedAc { newAccessor.GeneralGuidProp = oldAccessor.GeneralGuidProp; } + newAccessor.RealmValueProp = oldAccessor.RealmValueProp; + newAccessor.GeneralRealmValueProp = oldAccessor.GeneralRealmValueProp; } if (_propertyChanged != null) @@ -289,6 +293,10 @@ internal interface IIndexedClassAccessor : Realms.IRealmAccessor System.Guid GuidProp { get; set; } System.Guid GeneralGuidProp { get; set; } + + Realms.RealmValue RealmValueProp { get; set; } + + Realms.RealmValue GeneralRealmValueProp { get; set; } } [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] @@ -329,6 +337,18 @@ public System.Guid GeneralGuidProp get => (System.Guid)GetValue("GeneralGuidProp"); set => SetValue("GeneralGuidProp", value); } + + public Realms.RealmValue RealmValueProp + { + get => (Realms.RealmValue)GetValue("RealmValueProp"); + set => SetValue("RealmValueProp", value); + } + + public Realms.RealmValue GeneralRealmValueProp + { + get => (Realms.RealmValue)GetValue("GeneralRealmValueProp"); + set => SetValue("GeneralRealmValueProp", value); + } } [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] @@ -402,6 +422,28 @@ public System.Guid GeneralGuidProp } } + private Realms.RealmValue _realmValueProp; + public Realms.RealmValue RealmValueProp + { + get => _realmValueProp; + set + { + _realmValueProp = value; + RaisePropertyChanged("RealmValueProp"); + } + } + + private Realms.RealmValue _generalRealmValueProp; + public Realms.RealmValue GeneralRealmValueProp + { + get => _generalRealmValueProp; + set + { + _generalRealmValueProp = value; + RaisePropertyChanged("GeneralRealmValueProp"); + } + } + public IndexedClassUnmanagedAccessor(Type objectType) : base(objectType) { } @@ -416,6 +458,8 @@ public override Realms.RealmValue GetValue(string propertyName) "IntProp" => _intProp, "GuidProp" => _guidProp, "GeneralGuidProp" => _generalGuidProp, + "RealmValueProp" => _realmValueProp, + "GeneralRealmValueProp" => _generalRealmValueProp, _ => throw new MissingMemberException($"The object does not have a gettable Realm property with name {propertyName}"), }; } @@ -441,6 +485,12 @@ public override void SetValue(string propertyName, Realms.RealmValue val) case "GeneralGuidProp": GeneralGuidProp = (System.Guid)val; return; + case "RealmValueProp": + RealmValueProp = (Realms.RealmValue)val; + return; + case "GeneralRealmValueProp": + GeneralRealmValueProp = (Realms.RealmValue)val; + return; default: throw new MissingMemberException($"The object does not have a settable Realm property with name {propertyName}"); } @@ -487,6 +537,8 @@ protected override void SerializeValue(MongoDB.Bson.Serialization.BsonSerializat WriteValue(context, args, "IntProp", value.IntProp); WriteValue(context, args, "GuidProp", value.GuidProp); WriteValue(context, args, "GeneralGuidProp", value.GeneralGuidProp); + WriteValue(context, args, "RealmValueProp", value.RealmValueProp); + WriteValue(context, args, "GeneralRealmValueProp", value.GeneralRealmValueProp); context.Writer.WriteEndDocument(); } @@ -515,6 +567,12 @@ protected override void ReadValue(IndexedClass instance, string name, BsonDeseri case "GeneralGuidProp": instance.GeneralGuidProp = BsonSerializer.LookupSerializer().Deserialize(context); break; + case "RealmValueProp": + instance.RealmValueProp = BsonSerializer.LookupSerializer().Deserialize(context); + break; + case "GeneralRealmValueProp": + instance.GeneralRealmValueProp = BsonSerializer.LookupSerializer().Deserialize(context); + break; default: context.Reader.SkipValue(); break; diff --git a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/UnsupportedIndexableTypes.diagnostics.cs b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/UnsupportedIndexableTypes.diagnostics.cs index 992bd1e435..f165f3a8dd 100644 --- a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/UnsupportedIndexableTypes.diagnostics.cs +++ b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/UnsupportedIndexableTypes.diagnostics.cs @@ -57,22 +57,11 @@ { "Id": "RLM007", "Severity": 3, - "Message": "UnsupportedIndexableTypes.RealmvalueProp is marked as [Indexed] which is only allowed on integral types as well as string, bool and DateTimeOffset, not on RealmValue.", + "Message": "UnsupportedIndexableTypes.DecimalProp is marked as [Indexed] which is only allowed on integral types as well as string, bool and DateTimeOffset, not on decimal.", "Location": { "StartLine": 39, "StartColumn": 9, "EndLine": 40, - "EndColumn": 55 - } - }, - { - "Id": "RLM007", - "Severity": 3, - "Message": "UnsupportedIndexableTypes.DecimalProp is marked as [Indexed] which is only allowed on integral types as well as string, bool and DateTimeOffset, not on decimal.", - "Location": { - "StartLine": 42, - "StartColumn": 9, - "EndLine": 43, "EndColumn": 49 } }, @@ -81,9 +70,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.UnsupportedProp is of type 'int[]' which is not yet supported. If that is supposed to be a model class, make sure it implements IRealmObject/IEmbeddedObject/IAsymmetricObject.", "Location": { - "StartLine": 45, + "StartLine": 42, "StartColumn": 9, - "EndLine": 46, + "EndLine": 43, "EndColumn": 60 } }, @@ -92,9 +81,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.FtsIntProp is marked as [Indexed(IndexType.FullText)] which is only allowed on string properties, not on int.", "Location": { - "StartLine": 48, + "StartLine": 45, "StartColumn": 9, - "EndLine": 49, + "EndLine": 46, "EndColumn": 44 } }, @@ -103,9 +92,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.FtsBoolProp is marked as [Indexed(IndexType.FullText)] which is only allowed on string properties, not on bool.", "Location": { - "StartLine": 51, + "StartLine": 48, "StartColumn": 9, - "EndLine": 52, + "EndLine": 49, "EndColumn": 46 } }, @@ -114,9 +103,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.FtsRealmValueProp is marked as [Indexed(IndexType.FullText)] which is only allowed on string properties, not on RealmValue.", "Location": { - "StartLine": 54, + "StartLine": 51, "StartColumn": 9, - "EndLine": 55, + "EndLine": 52, "EndColumn": 58 } }, @@ -125,9 +114,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.FtsObjectProp is marked as [Indexed(IndexType.FullText)] which is only allowed on string properties, not on RealmObj.", "Location": { - "StartLine": 57, + "StartLine": 54, "StartColumn": 9, - "EndLine": 58, + "EndLine": 55, "EndColumn": 53 } }, @@ -136,9 +125,9 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.FtsDoubleProp is marked as [Indexed(IndexType.FullText)] which is only allowed on string properties, not on double.", "Location": { - "StartLine": 60, + "StartLine": 57, "StartColumn": 9, - "EndLine": 61, + "EndLine": 58, "EndColumn": 50 } }, @@ -147,31 +136,20 @@ "Severity": 3, "Message": "UnsupportedIndexableTypes.NoneIndexedInt is annotated as [Indexed(IndexType.None)] which is not allowed. If you don't wish to index the property, removed the [Indexed] attribute.", "Location": { - "StartLine": 63, + "StartLine": 60, "StartColumn": 9, - "EndLine": 64, + "EndLine": 61, "EndColumn": 48 } }, - { - "Id": "RLM007", - "Severity": 3, - "Message": "UnsupportedIndexableTypes.GeneralRealmValueProp is marked as [Indexed] which is only allowed on integral types as well as string, bool and DateTimeOffset, not on RealmValue.", - "Location": { - "StartLine": 66, - "StartColumn": 9, - "EndLine": 67, - "EndColumn": 62 - } - }, { "Id": "RLM028", "Severity": 3, "Message": "UnsupportedIndexableTypes.IndexedPrimaryKeyProp is marked has both [Indexed] and [PrimaryKey] attributes which is not allowed. PrimaryKey properties are indexed by default so the [Indexed] attribute is redundant.", "Location": { - "StartLine": 69, + "StartLine": 63, "StartColumn": 9, - "EndLine": 71, + "EndLine": 65, "EndColumn": 55 } } diff --git a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/TestClasses/IndexedClass.cs b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/TestClasses/IndexedClass.cs index adc6991251..38565570bd 100644 --- a/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/TestClasses/IndexedClass.cs +++ b/Tests/SourceGenerators/SourceGeneratorAssemblyToProcess/TestClasses/IndexedClass.cs @@ -40,5 +40,11 @@ internal partial class IndexedClass : IRealmObject [Indexed(IndexType.General)] public Guid GeneralGuidProp { get; set; } + + [Indexed] + public RealmValue RealmValueProp { get; set; } + + [Indexed(IndexType.General)] + public RealmValue GeneralRealmValueProp { get; set; } } } diff --git a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs index b78d7fb1a3..10ee2f3108 100644 --- a/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs +++ b/Tests/Weaver/AnalyticsAssembly/Generated/Realm.SourceGenerator/Realms.SourceGenerator.RealmGenerator/RootRealmClass_generated.cs @@ -36,16 +36,7 @@ static RootRealmClass() /// public static Realms.Schema.ObjectSchema RealmSchema = new Realms.Schema.ObjectSchema.Builder("RootRealmClass", ObjectSchema.ObjectType.RealmObject) { - Realms.Schema.Property.Object("JustForRef", "JustForObjectReference", managedName: "JustForRef"), - Realms.Schema.Property.ObjectList("ReferenceList", "JustForObjectReference", managedName: "ReferenceList"), - Realms.Schema.Property.PrimitiveList("PrimitiveList", Realms.RealmValueType.Int, areElementsNullable: false, managedName: "PrimitiveList"), - Realms.Schema.Property.ObjectDictionary("ReferenceDictionary", "JustForObjectReference", managedName: "ReferenceDictionary"), Realms.Schema.Property.PrimitiveDictionary("PrimitiveDictionary", Realms.RealmValueType.Int, areElementsNullable: false, managedName: "PrimitiveDictionary"), - Realms.Schema.Property.ObjectSet("ReferenceSet", "JustForObjectReference", managedName: "ReferenceSet"), - Realms.Schema.Property.PrimitiveSet("PrimitiveSet", Realms.RealmValueType.Int, areElementsNullable: false, managedName: "PrimitiveSet"), - Realms.Schema.Property.Primitive("Counter", Realms.RealmValueType.Int, isPrimaryKey: false, indexType: IndexType.None, isNullable: false, managedName: "Counter"), - Realms.Schema.Property.RealmValue("RealmValue", managedName: "RealmValue"), - Realms.Schema.Property.Backlinks("JustBackLink", "JustForObjectReference", "UseAsBacklink", managedName: "JustBackLink"), }.Build(); #region IRealmObject implementation @@ -94,30 +85,10 @@ void ISettableManagedAccessor.SetManagedAccessor(Realms.IRealmAccessor managedAc { if (!skipDefaults) { - newAccessor.ReferenceList.Clear(); - newAccessor.PrimitiveList.Clear(); - newAccessor.ReferenceDictionary.Clear(); newAccessor.PrimitiveDictionary.Clear(); - newAccessor.ReferenceSet.Clear(); - newAccessor.PrimitiveSet.Clear(); } - if (oldAccessor.JustForRef != null && newAccessor.Realm != null) - { - newAccessor.Realm.Add(oldAccessor.JustForRef, update); - } - newAccessor.JustForRef = oldAccessor.JustForRef; - Realms.CollectionExtensions.PopulateCollection(oldAccessor.ReferenceList, newAccessor.ReferenceList, update, skipDefaults); - Realms.CollectionExtensions.PopulateCollection(oldAccessor.PrimitiveList, newAccessor.PrimitiveList, update, skipDefaults); - Realms.CollectionExtensions.PopulateCollection(oldAccessor.ReferenceDictionary, newAccessor.ReferenceDictionary, update, skipDefaults); Realms.CollectionExtensions.PopulateCollection(oldAccessor.PrimitiveDictionary, newAccessor.PrimitiveDictionary, update, skipDefaults); - Realms.CollectionExtensions.PopulateCollection(oldAccessor.ReferenceSet, newAccessor.ReferenceSet, update, skipDefaults); - Realms.CollectionExtensions.PopulateCollection(oldAccessor.PrimitiveSet, newAccessor.PrimitiveSet, update, skipDefaults); - if (!skipDefaults || oldAccessor.Counter != default(Realms.RealmInteger)) - { - newAccessor.Counter = oldAccessor.Counter; - } - newAccessor.RealmValue = oldAccessor.RealmValue; } if (_propertyChanged != null) @@ -290,78 +261,12 @@ public bool TryGetPrimaryKeyValue(Realms.IRealmObjectBase instance, out RealmVal [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] internal interface IRootRealmClassAccessor : Realms.IRealmAccessor { - JustForObjectReference? JustForRef { get; set; } - - System.Collections.Generic.IList ReferenceList { get; } - - System.Collections.Generic.IList PrimitiveList { get; } - - System.Collections.Generic.IDictionary ReferenceDictionary { get; } - System.Collections.Generic.IDictionary PrimitiveDictionary { get; } - - System.Collections.Generic.ISet ReferenceSet { get; } - - System.Collections.Generic.ISet PrimitiveSet { get; } - - Realms.RealmInteger Counter { get; set; } - - Realms.RealmValue RealmValue { get; set; } - - System.Linq.IQueryable JustBackLink { get; } } [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] private class RootRealmClassManagedAccessor : Realms.ManagedAccessor, IRootRealmClassAccessor { - public JustForObjectReference? JustForRef - { - get => (JustForObjectReference?)GetValue("JustForRef"); - set => SetValue("JustForRef", value); - } - - private System.Collections.Generic.IList _referenceList = null!; - public System.Collections.Generic.IList ReferenceList - { - get - { - if (_referenceList == null) - { - _referenceList = GetListValue("ReferenceList"); - } - - return _referenceList; - } - } - - private System.Collections.Generic.IList _primitiveList = null!; - public System.Collections.Generic.IList PrimitiveList - { - get - { - if (_primitiveList == null) - { - _primitiveList = GetListValue("PrimitiveList"); - } - - return _primitiveList; - } - } - - private System.Collections.Generic.IDictionary _referenceDictionary = null!; - public System.Collections.Generic.IDictionary ReferenceDictionary - { - get - { - if (_referenceDictionary == null) - { - _referenceDictionary = GetDictionaryValue("ReferenceDictionary"); - } - - return _referenceDictionary; - } - } - private System.Collections.Generic.IDictionary _primitiveDictionary = null!; public System.Collections.Generic.IDictionary PrimitiveDictionary { @@ -375,60 +280,6 @@ public System.Collections.Generic.IDictionary PrimitiveDictionary return _primitiveDictionary; } } - - private System.Collections.Generic.ISet _referenceSet = null!; - public System.Collections.Generic.ISet ReferenceSet - { - get - { - if (_referenceSet == null) - { - _referenceSet = GetSetValue("ReferenceSet"); - } - - return _referenceSet; - } - } - - private System.Collections.Generic.ISet _primitiveSet = null!; - public System.Collections.Generic.ISet PrimitiveSet - { - get - { - if (_primitiveSet == null) - { - _primitiveSet = GetSetValue("PrimitiveSet"); - } - - return _primitiveSet; - } - } - - public Realms.RealmInteger Counter - { - get => (Realms.RealmInteger)GetValue("Counter"); - set => SetValue("Counter", value); - } - - public Realms.RealmValue RealmValue - { - get => (Realms.RealmValue)GetValue("RealmValue"); - set => SetValue("RealmValue", value); - } - - private System.Linq.IQueryable _justBackLink = null!; - public System.Linq.IQueryable JustBackLink - { - get - { - if (_justBackLink == null) - { - _justBackLink = GetBacklinks("JustBackLink"); - } - - return _justBackLink; - } - } } [EditorBrowsable(EditorBrowsableState.Never), Realms.Preserve(AllMembers = true)] @@ -436,85 +287,20 @@ private class RootRealmClassUnmanagedAccessor : Realms.UnmanagedAccessor, IRootR { public override ObjectSchema ObjectSchema => RootRealmClass.RealmSchema; - private JustForObjectReference? _justForRef; - public JustForObjectReference? JustForRef - { - get => _justForRef; - set - { - _justForRef = value; - RaisePropertyChanged("JustForRef"); - } - } - - public System.Collections.Generic.IList ReferenceList { get; } = new List(); - - public System.Collections.Generic.IList PrimitiveList { get; } = new List(); - - public System.Collections.Generic.IDictionary ReferenceDictionary { get; } = new Dictionary(); - public System.Collections.Generic.IDictionary PrimitiveDictionary { get; } = new Dictionary(); - public System.Collections.Generic.ISet ReferenceSet { get; } = new HashSet(RealmSet.Comparer); - - public System.Collections.Generic.ISet PrimitiveSet { get; } = new HashSet(RealmSet.Comparer); - - private Realms.RealmInteger _counter; - public Realms.RealmInteger Counter - { - get => _counter; - set - { - _counter = value; - RaisePropertyChanged("Counter"); - } - } - - private Realms.RealmValue _realmValue; - public Realms.RealmValue RealmValue - { - get => _realmValue; - set - { - _realmValue = value; - RaisePropertyChanged("RealmValue"); - } - } - - public System.Linq.IQueryable JustBackLink => throw new NotSupportedException("Using backlinks is only possible for managed(persisted) objects."); - public RootRealmClassUnmanagedAccessor(Type objectType) : base(objectType) { } public override Realms.RealmValue GetValue(string propertyName) { - return propertyName switch - { - "JustForRef" => _justForRef, - "Counter" => _counter, - "RealmValue" => _realmValue, - "JustBackLink" => throw new NotSupportedException("Using backlinks is only possible for managed(persisted) objects."), - _ => throw new MissingMemberException($"The object does not have a gettable Realm property with name {propertyName}"), - }; + throw new MissingMemberException($"The object does not have a gettable Realm property with name {propertyName}"); } public override void SetValue(string propertyName, Realms.RealmValue val) { - switch (propertyName) - { - case "JustForRef": - JustForRef = (JustForObjectReference?)val; - return; - case "Counter": - Counter = (Realms.RealmInteger)val; - return; - case "RealmValue": - RealmValue = (Realms.RealmValue)val; - return; - default: - throw new MissingMemberException($"The object does not have a settable Realm property with name {propertyName}"); - } + throw new MissingMemberException($"The object does not have a settable Realm property with name {propertyName}"); } public override void SetValueUnique(string propertyName, Realms.RealmValue val) @@ -524,29 +310,18 @@ public override void SetValueUnique(string propertyName, Realms.RealmValue val) public override IList GetListValue(string propertyName) { - return propertyName switch - { - "ReferenceList" => (IList)ReferenceList, - "PrimitiveList" => (IList)PrimitiveList, - _ => throw new MissingMemberException($"The object does not have a Realm list property with name {propertyName}"), - }; + throw new MissingMemberException($"The object does not have a Realm list property with name {propertyName}"); } public override ISet GetSetValue(string propertyName) { - return propertyName switch - { - "ReferenceSet" => (ISet)ReferenceSet, - "PrimitiveSet" => (ISet)PrimitiveSet, - _ => throw new MissingMemberException($"The object does not have a Realm set property with name {propertyName}"), - }; + throw new MissingMemberException($"The object does not have a Realm set property with name {propertyName}"); } public override IDictionary GetDictionaryValue(string propertyName) { return propertyName switch { - "ReferenceDictionary" => (IDictionary)ReferenceDictionary, "PrimitiveDictionary" => (IDictionary)PrimitiveDictionary, _ => throw new MissingMemberException($"The object does not have a Realm dictionary property with name {propertyName}"), }; @@ -562,15 +337,7 @@ protected override void SerializeValue(MongoDB.Bson.Serialization.BsonSerializat { context.Writer.WriteStartDocument(); - WriteValue(context, args, "JustForRef", value.JustForRef); - WriteList(context, args, "ReferenceList", value.ReferenceList); - WriteList(context, args, "PrimitiveList", value.PrimitiveList); - WriteDictionary(context, args, "ReferenceDictionary", value.ReferenceDictionary); WriteDictionary(context, args, "PrimitiveDictionary", value.PrimitiveDictionary); - WriteSet(context, args, "ReferenceSet", value.ReferenceSet); - WriteSet(context, args, "PrimitiveSet", value.PrimitiveSet); - WriteValue(context, args, "Counter", value.Counter); - WriteValue(context, args, "RealmValue", value.RealmValue); context.Writer.WriteEndDocument(); } @@ -581,22 +348,6 @@ protected override void ReadValue(RootRealmClass instance, string name, BsonDese { switch (name) { - case "JustForRef": - instance.JustForRef = Realms.Serialization.RealmObjectSerializer.LookupSerializer()!.DeserializeById(context); - break; - case "Counter": - instance.Counter = BsonSerializer.LookupSerializer>().Deserialize(context); - break; - case "RealmValue": - instance.RealmValue = BsonSerializer.LookupSerializer().Deserialize(context); - break; - case "ReferenceList": - case "PrimitiveList": - case "ReferenceSet": - case "PrimitiveSet": - ReadArray(instance, name, context); - break; - case "ReferenceDictionary": case "PrimitiveDictionary": ReadDictionary(instance, name, context); break; @@ -608,30 +359,13 @@ protected override void ReadValue(RootRealmClass instance, string name, BsonDese protected override void ReadArrayElement(RootRealmClass instance, string name, BsonDeserializationContext context) { - switch (name) - { - case "ReferenceList": - instance.ReferenceList.Add(Realms.Serialization.RealmObjectSerializer.LookupSerializer()!.DeserializeById(context)!); - break; - case "PrimitiveList": - instance.PrimitiveList.Add(BsonSerializer.LookupSerializer().Deserialize(context)); - break; - case "ReferenceSet": - instance.ReferenceSet.Add(Realms.Serialization.RealmObjectSerializer.LookupSerializer()!.DeserializeById(context)!); - break; - case "PrimitiveSet": - instance.PrimitiveSet.Add(BsonSerializer.LookupSerializer().Deserialize(context)); - break; - } + // No persisted list/set properties to deserialize } protected override void ReadDocumentField(RootRealmClass instance, string name, string fieldName, BsonDeserializationContext context) { switch (name) { - case "ReferenceDictionary": - instance.ReferenceDictionary[fieldName] = Realms.Serialization.RealmObjectSerializer.LookupSerializer()!.DeserializeById(context)!; - break; case "PrimitiveDictionary": instance.PrimitiveDictionary[fieldName] = BsonSerializer.LookupSerializer().Deserialize(context); break; diff --git a/Tests/Weaver/AssemblyToProcess/FaultyClasses.cs b/Tests/Weaver/AssemblyToProcess/FaultyClasses.cs index 7adb72e00a..2742094e24 100644 --- a/Tests/Weaver/AssemblyToProcess/FaultyClasses.cs +++ b/Tests/Weaver/AssemblyToProcess/FaultyClasses.cs @@ -98,13 +98,16 @@ public class IndexedProperties : RealmObject [Indexed] public DateTimeOffset DateTimeOffsetProperty { get; set; } + [Indexed] + public RealmValue RealmValueProperty { get; set; } + [Indexed(IndexType.General)] public ObjectId ObjectIdProperty { get; set; } [Indexed(IndexType.FullText)] public string? FullTextStringProperty { get; set; } - // This should cause an error: + // These should cause an error: [Indexed] public float SingleProperty { get; set; }