Skip to content

Commit

Permalink
Merge branch 'main' into ni/pkl
Browse files Browse the repository at this point in the history
* main:
  RNET-1134: Add obfuscation attribute on generated RealmSchema (#3594)
  Revert "Hide collections in mixed from public API" (#3606)
  Update to Core 14.7.0 (#3605)
  RNET-1145, RNET-1143: Fix reading schema from native (#3601)

# Conflicts:
#	.github/templates/test-weaver.yml
#	.github/workflows/test-weaver.yml
  • Loading branch information
nirinchev committed May 22, 2024
2 parents dedfcbe + f30ffdf commit 895ecc1
Show file tree
Hide file tree
Showing 161 changed files with 2,837 additions and 1,574 deletions.
22 changes: 19 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
## vNext (TBD)

### Enhancements
* None
* Added support for `Migration.FindInNewRealm` which is a helper that allows you to lookup the object in the post-migration Realm that corresponds to an object from the pre-migration Realm. (Issue [#3600](https://github.com/realm/realm-dotnet/issues/3600))
* Added `[System.Reflection.Obfuscation]` on the generated `RealmSchema` field to improve compatibility with obfuscation tools that change field and property names of generated classes. (Issue [#3574](https://github.com/realm/realm-dotnet/issues/3574))
* Added support for list and dictionaries of `RealmValue` (`IList<RealmValue>` and `IDictionary<string, RealmValue>`) 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<RealmValue> { 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))

### Fixed
* None
* Accessing `App.CurrentUser` from within a `User.Changed` notification would deadlock. (Core 14.7.0)
* Inserting the same link to the same key in a dictionary more than once would incorrectly create multiple backlinks to the object. This did not appear to cause any crashes later, but would have affected the value returned by `RealmObject.BacklinksCount` and queries involving backlinks counts. (Core 14.7.0)
* Fixed an issue that would cause `RealmObject.DynamicApi.GetList/Set/Dictionary` to fail when the collection contains primitive values. (Issue [#3597](https://github.com/realm/realm-dotnet/issues/3597))

### Compatibility
* Realm Studio: 15.0.0 or later.

### Internal
* Using Core x.y.z.
* Using Core 14.7.0.

## 12.1.0 (2024-05-01)

Expand Down
1 change: 1 addition & 0 deletions Realm/Realm.SourceGenerator/ClassCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ private string GeneratePartialClass()
var schema = @$"/// <summary>
/// Defines the schema for the <see cref=""{_classInfo.Name}""/> class.
/// </summary>
[System.Reflection.Obfuscation]
public static Realms.Schema.ObjectSchema RealmSchema = new Realms.Schema.ObjectSchema.Builder(""{_classInfo.MapTo ?? _classInfo.Name}"", {objectTypeString})
{{
{schemaProperties.Indent(trimNewLines: true)}
Expand Down
2 changes: 2 additions & 0 deletions Realm/Realm.UnityUtils/Initializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
//
////////////////////////////////////////////////////////////////////////////

using System.Reflection;
using System.Threading;
using Realms;
using Realms.PlatformHelpers;
using UnityEngine;

namespace UnityUtils
{
[Obfuscation(ApplyToMembers = true, Exclude = true)]
internal static class Initializer
{
private static int _isInitialized;
Expand Down
48 changes: 24 additions & 24 deletions Realm/Realm/DatabaseTypes/RealmValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private RealmValue(IDictionary<string, RealmValue> dict) : this()
/// <returns> A new RealmValue representing the input list. </returns>
/// <remarks> Once created, this RealmValue will just wrap the input collection.
/// After the object containing this RealmValue gets managed this value will be a Realm list.</remarks>
internal static RealmValue List(IList<RealmValue> value) => new(value);
public static RealmValue List(IList<RealmValue> value) => new(value);

/// <summary>
/// Gets a RealmValue representing a dictionary.
Expand All @@ -226,7 +226,7 @@ private RealmValue(IDictionary<string, RealmValue> dict) : this()
/// <returns> A new RealmValue representing the input dictionary. </returns>
/// <remarks> Once created, this RealmValue will just wrap the input collection.
/// After the object containing this RealmValue gets managed this value will be a Realm dictionary.</remarks>
internal static RealmValue Dictionary(IDictionary<string, RealmValue> value) => new(value);
public static RealmValue Dictionary(IDictionary<string, RealmValue> value) => new(value);

internal static RealmValue Create<T>(T value, RealmValueType type)
{
Expand Down Expand Up @@ -510,7 +510,7 @@ public string AsString()
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the underlying value is not of type <see cref="RealmValueType.List"/>.</exception>
/// <returns> A list representing the value stored in the database.</returns>
internal IList<RealmValue> AsList()
public IList<RealmValue> AsList()
{
EnsureType("List", RealmValueType.List);
return _listValue!;
Expand All @@ -521,7 +521,7 @@ internal IList<RealmValue> AsList()
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if the underlying value is not of type <see cref="RealmValueType.Dictionary"/>.</exception>
/// <returns> A dictionary representing the value stored in the database.</returns>
internal IDictionary<string, RealmValue> AsDictionary()
public IDictionary<string, RealmValue> AsDictionary()
{
EnsureType("Dictionary", RealmValueType.Dictionary);
return _dictionaryValue!;
Expand Down Expand Up @@ -1448,26 +1448,26 @@ public override int GetHashCode()
/// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
public static implicit operator RealmValue(RealmObjectBase? val) => val == null ? Null : Object(val);

///// <summary>
///// Implicitly constructs a <see cref="RealmValue"/> from <see cref="System.Collections.Generic.List{T}">List&lt;RealmValue&gt;?</see>.
///// </summary>
///// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
///// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
//public static implicit operator RealmValue(List<RealmValue>? val) => val == null ? Null : List(val);

///// <summary>
///// Implicitly constructs a <see cref="RealmValue"/> from <see cref="RealmValue">RealmValue[]?</see>.
///// </summary>
///// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
///// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
//public static implicit operator RealmValue(RealmValue[]? val) => val == null ? Null : List(val);

///// <summary>
///// Implicitly constructs a <see cref="RealmValue"/> from <see cref="System.Collections.Generic.Dictionary{TKey, TValue}">Dictionary&lt;string, RealmValue&gt;</see>.
///// </summary>
///// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
///// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
//public static implicit operator RealmValue(Dictionary<string, RealmValue>? val) => val == null ? Null : Dictionary(val);
/// <summary>
/// Implicitly constructs a <see cref="RealmValue"/> from <see cref="System.Collections.Generic.List{T}">List&lt;RealmValue&gt;?</see>.
/// </summary>
/// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
/// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
public static implicit operator RealmValue(List<RealmValue>? val) => val == null ? Null : List(val);

/// <summary>
/// Implicitly constructs a <see cref="RealmValue"/> from <see cref="RealmValue">RealmValue[]?</see>.
/// </summary>
/// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
/// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
public static implicit operator RealmValue(RealmValue[]? val) => val == null ? Null : List(val);

/// <summary>
/// Implicitly constructs a <see cref="RealmValue"/> from <see cref="System.Collections.Generic.Dictionary{TKey, TValue}">Dictionary&lt;string, RealmValue&gt;</see>.
/// </summary>
/// <param name="val">The value to store in the <see cref="RealmValue"/>.</param>
/// <returns>A <see cref="RealmValue"/> containing the supplied <paramref name="val"/>.</returns>
public static implicit operator RealmValue(Dictionary<string, RealmValue>? val) => val == null ? Null : Dictionary(val);

private void EnsureType(string target, RealmValueType type)
{
Expand Down
18 changes: 18 additions & 0 deletions Realm/Realm/Handles/SharedRealmHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ public static extern void rename_property(SharedRealmHandle sharedRealm,
[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_operating_system", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_operating_system(IntPtr buffer, IntPtr buffer_length);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "shared_realm_get_object_for_object", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_object_for_object(SharedRealmHandle realmHandle, ObjectHandle handle, out NativeException ex);

#pragma warning restore SA1121 // Use built-in type alias
#pragma warning restore IDE0049 // Use built-in type alias
}
Expand Down Expand Up @@ -677,6 +680,21 @@ public bool TryFindObject(TableKey tableKey, in RealmValue id, [MaybeNullWhen(fa
return true;
}

public bool TryFindObject(ObjectHandle handle, [MaybeNullWhen(false)] out ObjectHandle objectHandle)
{
var result = NativeMethods.get_object_for_object(this, handle, out var ex);
ex.ThrowIfNecessary();

if (result == IntPtr.Zero)
{
objectHandle = null;
return false;
}

objectHandle = new ObjectHandle(this, result);
return true;
}

public void RenameProperty(string typeName, string oldName, string newName, IntPtr migrationSchema)
{
NativeMethods.rename_property(this, typeName, (IntPtr)typeName.Length,
Expand Down
22 changes: 22 additions & 0 deletions Realm/Realm/Migration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,5 +115,27 @@ public void RenameProperty(string typeName, string oldPropertyName, string newPr

NewRealm.SharedRealmHandle.RenameProperty(typeName, oldPropertyName, newPropertyName, _migrationSchema);
}

/// <summary>
/// Finds an object obtained from <see cref="OldRealm"/> in <see cref="NewRealm"/>.
/// </summary>
/// <typeparam name="T">The type of the object in the new realm.</typeparam>
/// <param name="obj">The object obtained from the old realm.</param>
/// <returns>The corresponding object post-migration or <c>null</c> if the object no longer exists in the new realm.</returns>
/// <example>
/// <code>
/// foreach (var oldPerson in migration.OldRealm.DynamicApi.All("Person"))
/// {
/// var newPerson = migration.FindInNewRealm&lt;Person&gt;(oldPerson)
/// newPerson.Name = $"{oldPerson.DynamicApi.Get&lt;string&gt;("FirstName")} {oldPerson.DynamicApi.Get&lt;string&gt;("LastName")}";
/// }
/// </code>
/// </example>
public T? FindInNewRealm<T>(IRealmObject obj)
where T : IRealmObject
{
Argument.Ensure(obj.IsManaged, "Only managed RealmObject instances can be looked up in the new Realm", nameof(obj));
return NewRealm.FindExisting<T>(obj);
}
}
}
7 changes: 6 additions & 1 deletion Realm/Realm/Native/PrimitiveValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,12 @@ public static StringValue AllocateFrom(string? value, Arena arena)

public static implicit operator bool(in StringValue value) => value.data != null;

public static implicit operator string?(in StringValue value) => !value ? null : Encoding.UTF8.GetString(value.data, (int)value.size);
public static implicit operator string?(in StringValue value) => value.ToDotnetString();

public readonly string? ToDotnetString(bool treatEmptyAsNull = false)
=> data == null || (size == 0 && treatEmptyAsNull)
? null
: Encoding.UTF8.GetString(data, (int)size);
}

[StructLayout(LayoutKind.Sequential)]
Expand Down
23 changes: 19 additions & 4 deletions Realm/Realm/Realm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,21 @@ internal IQueryable<T> AllEmbedded<T>()
return default;
}

// This is only used during migrations. obj here is the object in the old realm, and we're trying to find
// its counterpart post-migration.
internal T? FindExisting<T>(IRealmObject obj)
{
ThrowIfDisposed();
Argument.Ensure(obj.IsManaged, "Only managed objects can be used in FindExisting", nameof(obj));
if (SharedRealmHandle.TryFindObject(obj.GetObjectHandle()!, out var objectHandle))
{
var metadata = Metadata[typeof(T).GetMappedOrOriginalName()];
return (T)MakeObject(metadata, objectHandle);
}

return default;
}

#endregion Quick Find using primary key

#region Thread Handover
Expand Down Expand Up @@ -1589,7 +1604,7 @@ internal Dynamic(Realm realm)
public IRealmObjectBase CreateObject(string className) => CreateObjectCore(className, primaryKey: null);

/// <summary>
/// Factory for a managed object without a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// Factory for a managed object with a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// </summary>
/// <returns>A dynamically-accessed Realm object.</returns>
/// <param name="className">The type of object to create as defined in the schema.</param>
Expand All @@ -1609,7 +1624,7 @@ internal Dynamic(Realm realm)
public IRealmObjectBase CreateObject(string className, long? primaryKey) => CreateObjectCore(className, primaryKey);

/// <summary>
/// Factory for a managed object without a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// Factory for a managed object with a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// </summary>
/// <returns>A dynamically-accessed Realm object.</returns>
/// <param name="className">The type of object to create as defined in the schema.</param>
Expand All @@ -1629,7 +1644,7 @@ internal Dynamic(Realm realm)
public IRealmObjectBase CreateObject(string className, string? primaryKey) => CreateObjectCore(className, primaryKey);

/// <summary>
/// Factory for a managed object without a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// Factory for a managed object with a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// </summary>
/// <returns>A dynamically-accessed Realm object.</returns>
/// <param name="className">The type of object to create as defined in the schema.</param>
Expand All @@ -1649,7 +1664,7 @@ internal Dynamic(Realm realm)
public IRealmObjectBase CreateObject(string className, ObjectId? primaryKey) => CreateObjectCore(className, primaryKey);

/// <summary>
/// Factory for a managed object without a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// Factory for a managed object with a primary key in a realm. Only valid within a write <see cref="Transaction"/>.
/// </summary>
/// <returns>A dynamically-accessed Realm object.</returns>
/// <param name="className">The type of object to create as defined in the schema.</param>
Expand Down
7 changes: 3 additions & 4 deletions Realm/Realm/Schema/Property.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,10 @@ public Property(string name, PropertyType type, string? objectType = null, strin
internal Property(in SchemaProperty nativeProperty)
{
Name = nativeProperty.name!;
string? managedName = nativeProperty.managed_name;
ManagedName = !string.IsNullOrEmpty(managedName) ? managedName! : Name;
ManagedName = nativeProperty.managed_name.ToDotnetString(treatEmptyAsNull: true) ?? Name;
Type = nativeProperty.type;
ObjectType = nativeProperty.object_type;
LinkOriginPropertyName = nativeProperty.link_origin_property_name;
ObjectType = nativeProperty.object_type.ToDotnetString(treatEmptyAsNull: true);
LinkOriginPropertyName = nativeProperty.link_origin_property_name.ToDotnetString(treatEmptyAsNull: true);
IsPrimaryKey = nativeProperty.is_primary;
IndexType = nativeProperty.index;
}
Expand Down
Loading

0 comments on commit 895ecc1

Please sign in to comment.