realm-dotnet icon indicating copy to clipboard operation
realm-dotnet copied to clipboard

[Unity][Bug] string.Contains() throws NotSupportedException in Unity 2021.2

Open sandr01d opened this issue 3 years ago • 3 comments

Realm Version: 10.7.1 Unity Version: 2021.2.5f1 Client OS: Windows 10 Pro 21H1 19043.1348

I recently upgraded my Unity version from 2020.3 to 2021.2. Since the update, calling string.Contains() in a realm query throws a NotSupportedException. With Unity 2020.3 results get returned as expected and no exception is thrown. My model looks like this:

public class Tag : RealmObject
{
    [PrimaryKey]
    public int Uid { get; set; }
    [Required]
    public string Name { get; set; }
}

and the class executing the query like this:

using Realms;
using System.IO;
using System.Linq;
using UnityEngine;

public class RealmExample : MonoBehaviour
{
    private static Realm realm;

    private void Awake()
    {
        RealmConfiguration config = new(Path.Combine(Application.dataPath, "database.realm"))
        {
            IsReadOnly = true,
            Schema = new[]
            {
                typeof(Tag),
            },
            SchemaVersion = 11ul
        };
        realm = Realm.GetInstance(config);
    }

    [ContextMenu("QueryTags")]
    public void QueryTags()
    {
        string substring = "T";
        IQueryable<Tag> tags = realm.All<Tag>()
                                    // The line below is the one causing the exception
                                    .Where(t => t.Name.Contains(substring, System.StringComparison.OrdinalIgnoreCase));

        foreach (var t in tags)
        {
            print(t.Name);
        }
    }
}

I receive the following exception:

NotSupportedException: The left-hand side of the Call operator must be a direct access to a persisted property in Realm.
Unable to process 'value(RealmExample+<>c__DisplayClass2_0).substring'.
Realms.RealmResultsVisitor.GetColumnName (System.Linq.Expressions.MemberExpression memberExpression, System.Nullable`1[T] parentType) (at <5ee71c2d3038455ca732a3febcca3879>:0)
Realms.RealmResultsVisitor.VisitMethodCall (System.Linq.Expressions.MethodCallExpression node) (at <5ee71c2d3038455ca732a3febcca3879>:0)
System.Linq.Expressions.MethodCallExpression.Accept (System.Linq.Expressions.ExpressionVisitor visitor) (at <61774763be294c9f8e2c781f10819224>:0)
System.Linq.Expressions.ExpressionVisitor.Visit (System.Linq.Expressions.Expression node) (at <61774763be294c9f8e2c781f10819224>:0)
Realms.RealmResultsVisitor.VisitMethodCall (System.Linq.Expressions.MethodCallExpression node) (at <5ee71c2d3038455ca732a3febcca3879>:0)
System.Linq.Expressions.MethodCallExpression.Accept (System.Linq.Expressions.ExpressionVisitor visitor) (at <61774763be294c9f8e2c781f10819224>:0)
System.Linq.Expressions.ExpressionVisitor.Visit (System.Linq.Expressions.Expression node) (at <61774763be294c9f8e2c781f10819224>:0)
Realms.RealmResults`1[T].GetOrCreateHandle () (at <5ee71c2d3038455ca732a3febcca3879>:0)
System.Lazy`1[T].ViaFactory (System.Threading.LazyThreadSafetyMode mode) (at <00c558282d074245ab3496e2d108079b>:0)
System.Lazy`1[T].ExecutionAndPublication (System.LazyHelper executionAndPublication, System.Boolean useDefaultConstructor) (at <00c558282d074245ab3496e2d108079b>:0)
System.Lazy`1[T].CreateValue () (at <00c558282d074245ab3496e2d108079b>:0)
System.Lazy`1[T].get_Value () (at <00c558282d074245ab3496e2d108079b>:0)
Realms.RealmCollectionBase`1+Enumerator[T]..ctor (Realms.RealmCollectionBase`1[T] parent) (at <5ee71c2d3038455ca732a3febcca3879>:0)
Realms.RealmCollectionBase`1[T].GetEnumerator () (at <5ee71c2d3038455ca732a3febcca3879>:0)
RealmExample.QueryTags () (at Assets/RealmExample.cs:32)

My current workaround is to use Like() instead of Contains() like this

// Added the asterisk to match the behaviour of Contains()
string substring = "*T*";
IQueryable<Tag> tags = realm.All<Tag>()
                            // This works fine
                            .Where(t => t.Name.Like(substring, false));

sandr01d avatar Dec 09 '21 14:12 sandr01d

Thank you for the report. I can reproduce the issue. And I can confirm that, as reported, it does not happen with version 2020.3. I'll start looking into it.

LaPeste avatar Dec 13 '21 17:12 LaPeste

Hi, are there any updates on this issue? I'm currently hitting the same issue in a Xamarin app. For me it seems to be a problem with the combination of using a variable for the text to search and the case insensitive query.

var searchText = "test";

// works
var result = realm.All<Person>().Where(x =>
    x.FirstName.Contains(searchText))
    .ToList();

// works
result = realm.All<Person>().Where(x =>
    x.FirstName.Contains("test", StringComparison.OrdinalIgnoreCase))
    .ToList();

// doesn't work
result = realm.All<Person>().Where(x =>
    x.FirstName.Contains(searchText, StringComparison.OrdinalIgnoreCase))
    .ToList();

eberthold avatar Sep 23 '22 07:09 eberthold

We haven't been able to take a look yet, but you could try working around it by using Like (as long as your searchText doesn't contain * or ?). An equivalent expression would be something like: .Where(x => x.FirstName.Like($"*{searchText}*", true). Alternatively, you can use the string query syntax like: .Filter("FirstName CONTAINS[c] $0", searchText).

nirinchev avatar Sep 23 '22 08:09 nirinchev

This is giving us test failures in a dotnet6 test project, on use of .Contains(s, StringComparison.CurrentCultureIgnoreCase). It is OK in 10.11.2 but throws in 10.12.0 and above.

System.MissingMethodException : Method not found: 'Boolean Realms.StringExtensions.Contains(System.String, System.String, System.StringComparison)'.

charlesroddie avatar Oct 04 '22 08:10 charlesroddie

.NET 6 has a built-in Contains(string, StringComparison) API, so you don't need to use the extension method from Realms.StringExtensions.

nirinchev avatar Oct 04 '22 09:10 nirinchev

OK thanks. We are on netstandard2.0 at the moment (for xamarin compatibility) so don't have access to this - just running the tests on dotnet6. We can probably stay on 10.11.2 for quite a long time until we can migrate to dotnet6+ so it's not urgent.

charlesroddie avatar Oct 06 '22 14:10 charlesroddie

Closing this as duplicate of https://github.com/realm/realm-dotnet/issues/3134

nirinchev avatar Jan 05 '23 22:01 nirinchev