Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with writing Lists to Firestore (only on Android) #392

Closed
cagriy opened this issue Dec 30, 2024 · 7 comments
Closed

Issue with writing Lists to Firestore (only on Android) #392

cagriy opened this issue Dec 30, 2024 · 7 comments

Comments

@cagriy
Copy link

cagriy commented Dec 30, 2024

Please note there was a similar issue in the past, I cannot tell of this was related: #276

Versions:
Plugin.Firebase.Firestore: 3.1.0
AdamE.Firebase.iOS.Installations: 10.29.0.1

Object definition:

public sealed class UserModel : IFirestoreObject
{
    [FirestoreDocumentId] 
    public string DocumentId { get; set; }

    [FirestoreProperty(nameof(DocumentReference))]
    public IDocumentReference DocumentReference { get; set; }

    [FirestoreProperty(nameof(SchoolLevel))]
    public IList<int> Level { get; set; }

I then run

        IDocumentReference docReference =
            CrossFirebaseFirestore.Current
                .GetCollection("Users")
                .CreateDocument();
        var user = new UserModel()
        {
            DocumentReference = docReference,
            Level = new List<int> { 1 , 2 , 3 }
        };
        await docReference.SetDataAsync(user);

On IOS, the array (list) gets written to FireStore correctly, on Android, it creates an array with a single element, using the first item.

Please let me know if there is anything else I can provide to help.

@cagriy
Copy link
Author

cagriy commented Jan 7, 2025

@TobiasBuchholz @AdamEssenmacher , would you have an opinion on how I can troubleshoot this issue please?

@AdamEssenmacher
Copy link
Collaborator

@cagriy can you try cloning the repo and step through a scenario with a debugger? It sounds like there might be a bug somewhere when the plugin is translating from pure .NET types to .NET for Android types used by the native platform SDK.

@cagriy
Copy link
Author

cagriy commented Jan 12, 2025

Hi @AdamEssenmacher , I debugged as suggested, and the problem seems to be here:
https://github.com/TobiasBuchholz/Plugin.Firebase/blob/master/src/Firestore/Platforms/Android/Extensions/ListExtensions.cs#L36

Here I am writing a list of 7 elements (DateTimeOffset), iteration works as expected and item.ToJavaObject(); returns the correct value. However list.Add(item.ToJavaObject()) stops adding new elements to the list after one or two iterations, although it goes through all 7.

    public static JavaList ToJavaList(this IEnumerable @this)
    {
        var list = new JavaList();
        foreach(var item in @this) {
            var newItem = item.ToJavaObject();
            list.Add(newItem);
        }
        return list;
    }

I changed it as above and monitored newItem in all seven operations, it was fine. But the list stopped growing after one or two iterations.

Out of curiosity, I changed this function to:

        var myList = new List<object>();
        foreach (var item in @this)
        {
            var newItem = item.ToJavaObject();
            myList.Add(newItem);
        }
        
        var list = new JavaList(myList);
        return list;

It then started adding all seven elements consistently. I am not suggesting this as a fix, but it may give us a clue. Essentially, JavaList calls the same Add method in its constructor. If there is a genuine reason for the latter to work, I can PR it.

Please let me know what the best way to proceed is.

@AdamEssenmacher
Copy link
Collaborator

AdamEssenmacher commented Jan 12, 2025

I was about to tell you that I couldn't reproduce based on your original sample code Level = new List<int> { 1 , 2 , 3 }, but what you said about "one or two" iterations working with timestamps led me to a hunch that ended up being right.

If you update your original sample to Level = new List<int> { 1 , 2 , 2 } you'll find that what makes it into Firestore is {1, 2}. What's happening is that duplicates are not being added to the list.

JavaList.Add(item) returns a bool indicating whether the add was successful. Sure enough, it returns false when you try to add an existing item to the list.

JavaList is part of the .NET Android Runtime--so this behavior has nothing to do with the native Firestore SDK or Firestore bindings.

The Android .NET JavaList type wraps the native Java ArrayList, which is expected to always return true when calling Add(item). The boolean return value is there because ArrayList implements the Collection interface.

So I took a look at the Android.Runtime code itself... and sure enough:

https://github.com/dotnet/android/blob/dff6de5202b8496b82b83738700ef520e8d20563/src/Mono.Android/Android.Runtime/JavaList.cs#L548-L559

For whatever reason, the JavaList .NET wrapper type for ArrayList changes the expected behavior by checking for and rejecting duplicate values. This is almost certainly not correct--especially since, as you found, the behavior is inconsistent when adding elements to JavaList in any other way (e.g. constructor, AddAll()).

What's incredible about this is that the offending code has been there since at least when Xamarin went open source in 2016!

I'll open an issue at dotnet/android. In the meantime, for this plugin, the alternative solution you found seems like a good enough workaround. I'd probably leave a comment behind with a link back to this issue and a short explanation.

@cagriy
Copy link
Author

cagriy commented Jan 13, 2025

For clarification, I was testing with seven identical DateTimeOffsets yesterday, and I was typically getting one or two elements, on a single occasion, three.

cagriy pushed a commit to cagriy/Plugin.Firebase that referenced this issue Jan 13, 2025
This is to avoid an issue in dotnet/android that is resulting in incosistent
behaviour when a List has duplicate items.

TobiasBuchholz#392
@cagriy
Copy link
Author

cagriy commented Jan 16, 2025

@TobiasBuchholz , I apologise for my impatience. Would it be possible to have this one merged / released, if possible at all?

@TobiasBuchholz
Copy link
Owner

No worries, the new version is out now! 🚀 ✌

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants