1

I’m working on a project where I need to set-up a listener on a document, so I can poll the results of specific map fields on it as they change. These are tracking data aggregations from questions - example structure attached. This is working fine, as per the code below.

[FirestoreData]
public class Question1
{
   
    [FirestoreProperty("I do")]
    public string beer { get; set; }

    [FirestoreProperty("I don't")]
    public string wine { get; set; }
}

public void getDataFeed()
{
 
    FirebaseFirestore db = FirebaseFirestore.DefaultInstance;
    DocumentReference aggs = db.Collection("aggregations").Document("s1e1");

   ListenerRegistration aggregation =  aggs.Listen(snapshot =>
    {
       Question1 question1 = snapshot.GetValue<Question1>("questions.q3");
       Debug.Log(question1.beer);
       Debug.Log(question1.wine);
    });
}

However, I won’t know in advance exactly what the fields to be queried will be, as it’s running as part of a live event.

Is it possible to dynamically create a FirestoreData class at runtime to match a dynamically created query path?

Or...enter image description here

Is there simply a better approach for the problem I'm trying to solve?

Many thanks in advance.

Infosunny
  • 459
  • 4
  • 17
  • Instead of dynamically trying to generate a `Question1` class, did you consider using `snapshot.GetValue("questions.q3")` without a type? That should give you a dictionary with the values, which you can then access by name or dynamically like shown here: https://stackoverflow.com/questions/1276763/how-do-i-get-the-list-of-keys-in-a-dictionary – Frank van Puffelen Nov 21 '21 at 21:39
  • I hadn't considered that! That would be 100 times simpler :) But... I don't seem to be able to use GetValue without a type? ` Dictionary data = snapshot.GetValue("questions.q3");` throws an error.. – Iain Simons Nov 21 '21 at 21:46
  • From https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document it seems that might be called `snapshot.ToDictionary`. – Frank van Puffelen Nov 21 '21 at 21:47
  • So, as I understand it, I can't actually target that map object using Document Reference? eg : `DocumentReference aggs = db.Collection("aggregations").Document("s1e1.questions.q3");` In which case, perhaps a better approach is to parse the whole document to dictionaries and parse those? I was hoping to be able to target the single map object, but perhaps this points to a rethink of structure... – Iain Simons Nov 21 '21 at 21:56
  • Ah, I assumed that was your document ID. There is indeed no way to load only some fields of a document. If you need that, it is typically a moment to consider using a subcollection. – Frank van Puffelen Nov 21 '21 at 22:13

1 Answers1

1

In this case, instead of dynamically creating classes the best approach would be mapping the objects with dictionaries, that map values can be nested, and you can mix and match approaches. For example, an attributed class can contain a dictionary or vice versa. Similarly, you can serialize as an anonymous type then deserialize as an attributed class. This is an example from:

https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Firestore/latest/datamodel#mapping-with-attributed-classes

    FirestoreDb db = FirestoreDb.Create(projectId);
    // Create a document with a random ID in the "cities" collection.
    CollectionReference collection = db.Collection("cities");
    Dictionary<string, object> city = new Dictionary<string, object>
    {
        { "Name", "Los Angeles" },
        { "Country", "USA" },
        { "State", "CA" },
        { "Capital", false },
        { "Population", 3900000L }
    };
    DocumentReference document = await collection.AddAsync(city);
    
    // Fetch the data back from the server and deserialize it.
    DocumentSnapshot snapshot = await document.GetSnapshotAsync();
    Dictionary<string, object> cityData = snapshot.ToDictionary();
    Console.WriteLine(cityData["Name"]); // Los Angeles
Pablo
  • 68
  • 5