question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

C# FirebaseDataBase API - Real Time Get Data into a List

See original GitHub issue

I´m new in this topic about Firebase in C#, the online documetation is bad, I only find 2 (FireSharp and FireBaseDataBase) API for connect my Desktop App with Firebase and also I try to connect my app with JSON HttpRequest. In all the case when i try to PUT, UPDATE, DELETE, the response time delay 4 seconds and i´m sure that my internet have a good Connection. On the other hand and as a main question (using the API FirebaseDataBase for C# WPF) is why i can´t put the real time data in a List(). First I try to do this.


//Example//`
public void StreamData()
{
List group= new List();
var firebase = new FirebaseClient("https://dinosaur-facts.firebaseio.com/");
var observable = firebase
.Child("dinosaurs")
.AsObservable()
.Subscribe(d => group.Add(d.Object));
}

But the problem here is that “group” dont Add “d.Object”. Then I try to use this code:

public void StreamData()
{
    var observable = firebaseClient.Child("Information").AsObservable<Persona>();
    var t=  observable.ObserveOnDispatcher().AsObservableCollection();
}

But I have this problem :

System.InvalidOperationException: ‘The current thread has no Dispatcher associated with it.’

In summary i try to get a real time data from Firebase with the API FireBaseDataBase, as a List() because i want to insert in my Datagrid.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:1
  • Comments:15 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
TennisDrumcommented, Mar 19, 2019

I’m pretty new to Xamarin and definitely to FirebaseDatabase.net API, but have figured it out. Here’s all the details on how I got this to work, including getting ListViews to be automatically updated from a Firebase subscription. Warning - this is a pretty long post. I’ve tried to provide ALL the details. Again - I’m new to C# and Xamarin, so I may not refer to things the “proper” way. Hopefully, I haven’t left out anything important. I’ll monitor this thread to see if I can help if you have questions/problems. Also, I hope I don’t offend everyone with my basic explanations - you probably already know most of the core stuff here.

Overview of the process:

  • I’m storing some of the firebase constants (URL, APIKey in public static strings so they are available throughout my app, anytime I need to get to the database)
  • I have a Class of objects called “Event”. Events have properties
  • The Event Class uses InotifyPropertyChanged to automate the updates; this also means the class properties have to be created a specific way to work properly
  • After creating all of the properties within the class, I create an ObservableCollection of Events
  • Somewhere in your app, you need to log the user into your database
  • Once logged in, you’ll start the subscription to the data. The retrieved data is initially captured in an IDisposable that I’ve declared in my Globals class. That data is then added to the ObservableCollection depending on whether it is an add, delete or modify from the database.
  • At this point, you have an ObservableCollection that contains your data and is constantly being kept current by the subscription
  • In your xaml page, create your ListView. Bind elements to the class properties
  • In your xaml.cs code behind file, designate the ListView’s ItemsSource as the ObservableCollection

Now for all the code - just in case you can’t follow what I just typed.

In my Helpers.cs file:

namespace MyApp.Classes
{
    public class Globals
    {
        public static string firebaseUrl = "https://xamarinfirebasetest-XXXXXX.firebaseio.com/";

        // the API Key can be found via the console for the project
        public static string firebaseApiKey = "\xxxxxxxxxx-xxxxxxxxxxxxxx";

        // create a public variable for the "listener" that will .Subscribe to Firebase database
        public static IDisposable returnedEvents;

    }

}

In EventClass.cs: My sample object class using INotifyPropertyChanged - this allows binding and the list view will stay current

namespace MyApp.Classes

{
    public class EventsClass
    {
    
    public class Event : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler PropertyChanged;

        //  in order to tell the system to monitor this property for changed, we have to FULLY create the property with a "backing field"
        private string _eventName;
        private string _lowOrHigh;
        //rest of them....

        public string EventName
        {
            get { return _eventName; }

            set
            {
                // if the value of name has not changed, don't do anything
                if (_eventName == value)
                    return;

                // else, set the name to the new value
                _eventName = value;
                // now raise the property changed event - this involves delegates
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(nameof(EventName)));

            }

        }

        public string LowOrHigh
        {
            get { return _lowOrHigh; }

            set
            {
                // if the value of name has not changed, don't do anything
                if (_lowOrHigh == value)
                    return;

                // else, set the name to the new value
                _lowOrHigh = value;
                // now raise the property changed event - this involves delegates
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(nameof(LowOrHigh)));

            }

        }
// rest of them ...

// then create the ObservableCollection of Event objects
public static ObservableCollection<Event> _returnedEvents = new ObservableCollection<Event>();

Somewhere in your app, you need to authorize the user (login) to the database. In my case, I’ve included try/catch in order to display error messages to the user

using Firebase.Database;

using Firebase.Auth;


async void SignIn_Clicked(object sender, System.EventArgs e)
        {

            try
            {
                var auth = new FirebaseAuthProvider(new FirebaseConfig(firebaseApiKey));
                var firebaseUsername = userNameEntry.Text;
                var firebasePassword = passwordEntry.Text;

                // this grabs a one-off token using the username and password combination
                var token = await auth.SignInWithEmailAndPasswordAsync(firebaseUsername, firebasePassword);

                // finally log in
                // then use this FirebaseClient to send and receive data via the authenticated user
                // assumes the firebase variable has already been declared as Public
                App.firebase = new FirebaseClient(
                                        firebaseUrl,
                                        new FirebaseOptions
                                        {
                                            AuthTokenAsyncFactory = () => Task.FromResult(token.FirebaseToken)
                                        });
            }

            catch (FirebaseAuthException ex)
            {
                if (ex.Reason.ToString() == "WrongPassword")
                {
                    loginErrorLabel.Text = "Wrong Password. Please try again.";
                    loginErrorLabel.BackgroundColor = System.Drawing.Color.Yellow;

                    return;

                }

                if (ex.Reason.ToString() == "UnknownEmailAddress")
                {
                    loginErrorLabel.Text = "Unknown email. Please try again.";
                    loginErrorLabel.BackgroundColor = System.Drawing.Color.Yellow;
                    return;

                }

                if (ex.Reason.ToString() == "InvalidEmailAddress")
                {
                    loginErrorLabel.Text = "Invalid email. Please try again.";
                    loginErrorLabel.BackgroundColor = System.Drawing.Color.Yellow;
                    return;

                }

            }

            // made it through the try/catch - login was successful
            
        }

Somewhere in your app, you will need to start the subscription to the database I experienced a performance problem (30 second delay for the first pull of data) if I started this as a subscription. So, instead I pull the data one time (almost instantly), and then start the subscription:

using Firebase.Database;

using Firebase.Database.Query;

async void listenForEvents()
        {
            // clear the observableCollection so data doesn't double up
            _returnedEvents.Clear();

            // to help speed things up, first retrieve the data - THEN start the subscription
            var test = await App.firebase
                .Child("Events")
                .OnceAsync<Event>();

            for (int i = 0; i < test.Count; i++)
            {
                _returnedEvents.Add(test.ElementAt(i).Object);
            }

            // now start the subscription
            returnedEvents = App.firebase
            .Child("Events")
            .AsObservable<Event>()
            .Subscribe(eventReceived =>
            {
                // check to see what type of FirebaseEvent was detected by the subscription
                if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.Delete)
                    // record deleted - remove from ObservableCollection
                    _returnedEvents.Remove(eventReceived.Object);

                // record was added or updated
                if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
                {
                    // see if the inserted/updated object is already in our ObservableCollection
                    var found = _returnedEvents.FirstOrDefault(i => i.Key == eventReceived.Key);

                    if (found == null)
                    {
                        //  is NOT in the observableCollection - add it
                        _returnedEvents.Add(eventReceived.Object);
                    }
else
                    {
                        // event was updated 
                        int tempIndex = _returnedEvents.IndexOf(found);
                        _returnedEvents[tempIndex] = eventReceived.Object;
                    }
                }

            });

        }

Almost there! At this point, the ObservableCollection has your data from the database and will be kept in sync now you just need to connect that ObservableCollection to your ListView.

On your xaml page, create your ListView, binding to the properties of your class:

            <ListView x:Name="eventListView" HeightRequest="200" HorizontalOptions="Center" >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal">
                                <Label Text="{Binding EventName}" VerticalOptions="Center"/>
                                <Label Text=" " />
                                <Label Text="{Binding LowOrHigh}" VerticalOptions="Center" />
                                <Label Text=" " />
                                // on and on....
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>

FINALLY - on your xaml.cs code behind page, set the ListView’s ItemsSource to the ObservableCollection:

public ManageEventsPage()
        {
            InitializeComponent();

            eventListView.ItemsSource = _returnedEvents;
            
        }

Let the questions begin!

1reaction
fabrixiop26commented, Apr 20, 2019

@fabricciotc Hey I tried your method but I am getting this error: “The current thread has no Dispatcher associated with it”, also I have tried what @TennisDrum posted but my DataGrid does not updates, although the changes in the Firebase are detected by .Subscribe(), my ObserVableCollection remains the same as when I first retrieve the data

Read more comments on GitHub >

github_iconTop Results From Across the Web

Work with Lists of Data on the Web - Firebase - Google
You can use the Realtime Database Query class to retrieve data sorted by key, by value, or by value of a child. You...
Read more >
Retrieving Data | Firebase Realtime Database - Google
This document covers the basics of retrieving database data, how data is ordered, and how to perform simple queries on data. Data retrieval...
Read more >
C# FirebaseDataBase API - Real Time Get Data into a List
In summary i try to get a real time data from Firebase with the API FireBaseDataBase, as a List() because i want to...
Read more >
How to Save Data to the Firebase Realtime Database in ...
Inside that column Navigate to Firebase Realtime Database. Click on that option and you will get to see two options on Connect app...
Read more >
Best Practices: Arrays in Firebase
This article will explain Firebase's array support, the ins-and-outs of arrays in distributed data, and some common techniques for dealing with ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found