System.Collections.Generic.Dictionary '2 { Firebase Realtime Database } - Error - c#

I learning how to use Firebase Realtime Database with unity games...
in Sending data from the game to the database and get the data to the game from the database...
send data work good.
but I have a problem getting data from the database...
the output of the sss (the text that the result will show in) should be 4... the same value in the database... but it shows:(System.Collections.Generic.Dictionary`2[System.String,System.Object])
is there any solution for this?
here is a code on C# to get the data:
private IEnumerator LoadUserData()
{
var DBTask = dbRefence.Child("GameReview").GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
DataSnapshot snapshot = DBTask.Result;
sss.text = snapshot.Value.ToString();
}

Related

Firebase Realtime Database Retrieved Single Data on Xamarin Android

I successfully get the data from realtime database firebase when I put break point on OndataChange but when I put the break point after the OnDataChange method the records are now null.
public void OnDataChange(DataSnapshot dataSnapshot)
{
if (dataSnapshot.Value != null)
{
reclist.Clear();
TempDB rec = new TempDB();
rec.ID = dataSnapshot.Key;
rec.Firstname = dataSnapshot.Child("FirstName").Value.ToString();
rec.Lastname = dataSnapshot.Child("LastName").Value.ToString();
rec.Address = dataSnapshot.Child("Address").Value.ToString();
rec.ContactNo = dataSnapshot.Child("Contact number").Value.ToString();
rec.Email = dataSnapshot.Child("EmailAddress").Value.ToString();
rec.Password = dataSnapshot.Child("Password").Value.ToString();
idd = rec.ID;
Console.WriteLine(id.ToString());
reclist.Add(rec);
}
}
That's because data is loaded from Firebase asynchronously, while the rest of your code continues. Then once the data is loaded, your onDataChange is called with it. This means that the code just after what you shared indeed runs before the code inside the onDataChange, and that is working as intended.
The solution is always the same: any code that needs the data from the database needs to be inside onDataChange, be called from there, or be otherwise synchronized.
Have a look at these two (Android) examples for some ideas:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener

How to load a scene when it completes getting data from firebase?

there are two scenes in my game.
first one is "main Scene" and second one is "user Data Scene".
in "user data scene" I am retrieving current user data from firebase.
The problem is when I switch to "user data scene" from "main scene" there are no data for few seconds until its retrieved from firebase. it looks very bad to me.
All I want is when user is on "main scene" and try to open "user data scene" it should be not active until retrieving data from firebase is done.
I attached the script that gets data from firebase to the main scene's empty object which does not destroy on load. and calling it in start function of the "user data scene".
I am calling below function in start function of "user data scene".
I want to wait until data is loaded from firebase and value is attached to text objects before activating the "user data scene" ?
public IEnumerator getUserDataFromDb(Text userNameTxt, Text totalDiamondTxt, Text totalGoldText) //get data from database of diamond and how much diamond has been sold
{
userNameTxt.text = FirebaseAuth.DefaultInstance.CurrentUser.DisplayName;
var getDbTask = FirebaseDatabase.DefaultInstance.GetReference("Users").Child(FirebaseAuth.DefaultInstance.CurrentUser.UserId).GetValueAsync();
yield return new WaitUntil(predicate: () => getDbTask.IsCompleted);
if (getDbTask.Exception != null)
{
Debug.LogWarning(getDbTask.Exception.InnerExceptions[0].InnerException.Message);
}
else
{
DataSnapshot snapshot = getDbTask.Result;
totalDiamondTxt.text = snapshot.Child("Sell").Child("Total").Child("TotalDiamond").Value.ToString();
int dbDiamondValue = int.Parse(snapshot.Child("Sell").Child("Total").Child("TotalDiamond").Value.ToString());
totalGoldText.text = snapshot.Child("Sell").Child("Total").Child("TotalGold").Value.ToString();
int dbGoldValue = int.Parse(snapshot.Child("Sell").Child("Total").Child("TotalGold").Value.ToString());
}
}
Maybe something like this:
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
db = Firebase.Firestore.FirebaseFirestore.DefaultInstance;
//use your data and load your secene after
DocumentReference docRef =
db.Collection("yourCollection").Document("yourDocumentCode");
docRef.GetSnapshotAsync().ContinueWithOnMainThread(task => {
DocumentSnapshot snapshot = task.Result;
if (snapshot.Exists) {
//do you stuff here after retrieving the info
} else {
Debug.Log(String.Format("Document {0} does not exist!", snapshot.Id));
}
});
} else {
UnityEngine.Debug.LogError(System.String.Format("Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
Need that for the specific use of firestore, but you got a similar snippet in the documentation so that right after you do whatever you need with the firebase services you move on executing your app.

How can I use a C# list within signalr javascript file?

My ASP.NET project is using signalr to continuously update a web page every second with new information that enters the database. So the web page is only receiving information, not sending it. I have successfully been able to send an array from the hub towards my javascript file, and my web page correctly displayed all of the information. However, I would like to achieve the same result by using a C# list.
I have a Hub that looks like the following, and I am trying to send a C# list of items that is fetched from the GetListOfItems() method toward the javascript file:
public class DatabaseDisplayHub : Hub
{
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
await UpdateDatabase();
}
/// <summary>
/// Sends the updated information to the clients connected to the web interface.
/// </summary>
/// <returns></returns>
public async Task UpdateDatabase()
{
while (true)
{
await Clients.All.SendAsync("UpdateDatabase", Startup._eventsDatabase.GetListOfItems());
Thread.Sleep(1000);
}
}
}
My javascript file receives the list as a parameter to the function when a connection is established, and then tries to access several of the item's fields to display them on the HTML page.
"use strict";
// Connects to the DaabaseDisplayHub.cs to be automatically updated with the new information to display.
var connection = new signalR.HubConnectionBuilder().withUrl("/databaseDisplayHub").build();
// Called when the DatabaseDisplayHub sends the new information to display to the connected clients.
connection.on("UpdateDatabase", function (eventList) {
var eventsBody = document.getElementById("eventsBody");
while (eventsBody.firstChild) {
eventsBody.removeChild(eventsBody.firstChild);
}
eventList.foreach(eventItem => {
var eventsRow = document.createElement("tr");
eventsBody.appendChild(eventsRow);
var eventIdentifier_TD = document.createElement("td");
eventIdentifier_TD.innerHTML = eventItem.eventIdentifier;
eventsRow.appendChild(eventIdentifier_TD);
var EventActive_TD = document.createElement("td");
EventActive_TD.innerHTML = eventItem.eventActive;
eventsRow.appendChild(EventActive_TD);
var Car_TD = document.createElement("td");
Car_TD.innerHTML = eventItem.car;
eventsRow.appendChild(Car_TD);
var StartTimeStamp_TD = document.createElement("td");
StartTimeStamp_TD.innerHTML = eventItem.startTimeStamp;
eventsRow.appendChild(StartTimeStamp_TD);
var EndTimeStamp_TD = document.createElement("td");
EndTimeStamp_TD.innerHTML = eventItem.endTimeStamp;
eventsRow.appendChild(EndTimeStamp_TD);
});
});
// Called when the connection with the Hub is closed.
connection.onclose(function () {
alert("Disconnected from server.");
console.log("Database Page Disconnected.");
});
// Connects the WebSocket to the DatabaseDisplayHub.
connection.start().then(function () {
console.log("Connected to database hub");
}).catch(function (err) {
alert("Error while connecting to server.");
return console.error(err.toString());
});
I have tried debugging the javascript code through chrome developer tools, but I can't seem to understand why the list fields are not being displayed. I'm wondering whether the C# list is not compatible inside the javascript file. I have searched everywhere and can't seem to find a solution to be able to access the contents of the list.
Any help would be greatly appreciated. Thank you!
Generally, I take a C# List and serialize it like this:
Clients.Caller.ReportData(new JavaScriptSerializer().Serialize(reporthosttable));
In my client JS file receive that like this:
chat.client.reportData = function (reportData) {
var data = JSON.parse(reportData);
//do stuff with data
}
Other solutions exist I am sure... this is what works for me in a similar scenario where there are large data sets queried from a database, converted to a C# List and then send to client for display on a repeated basis.
I managed to figure this out thanks to the nudge received from one of the answers below.
I used the JsonConvert.SerializeObject() method found within the Newtonsoft.Json package to convert my C# list to a Json string.
// Within my Hub class
await Clients.All.SendAsync("UpdateDatabase", JsonConvert.SerializeObject(Startup._eventsDatabase.GetListOfItems()));
Then, within my JavaScript file, I had to parse that string using JSON.parse() for it to be in the proper form so that I can access each object's properties.
// Within my JavasScript file
connection.on("UpdateDatabase", function (eventList) {
var eventObject = JSON.parse(eventList);
var eventsBody = document.getElementById("eventsBody");
while (eventsBody.firstChild) {
eventsBody.removeChild(eventsBody.firstChild);
}
eventObject.forEach(function(eventItem) {
var eventsRow = document.createElement("tr");
eventsBody.appendChild(eventsRow);
var eventIdentifier_TD = document.createElement("td");
eventIdentifier_TD.innerHTML = eventItem.EventIdentifier;
eventsRow.appendChild(eventIdentifier_TD);
var EventActive_TD = document.createElement("td");
EventActive_TD.innerHTML = eventItem.EventActive;
eventsRow.appendChild(EventActive_TD);
var Car_TD = document.createElement("td");
Car_TD.innerHTML = eventItem.Car;
eventsRow.appendChild(Car_TD);
var StartTimeStamp_TD = document.createElement("td");
StartTimeStamp_TD.innerHTML = eventItem.StartTimeStamp;
eventsRow.appendChild(StartTimeStamp_TD);
var EndTimeStamp_TD = document.createElement("td");
EndTimeStamp_TD.innerHTML = eventItem.EndTimeStamp;
eventsRow.appendChild(EndTimeStamp_TD);
});
});

How to download data from Firebase?

I'm attempting to retrieve some data from a Firebase database. I've been able to do it fine in the past, but there's something wrong with my GetValueAsync() code below. When debugging it gets stuck at the "await reference.Database" line, but I'm not sure what I'm doing wrong. When running without debugging, none of the information is ever retrieved.
I'm uncertain if the problem is with the path, or the await/async function. Debugging shows that loggedUserId is storing the value before referencing it in the next line, but the rest of the function never completes or faults. The application compiles but I'm never able to capture any info from the snapshot.
The format of my database is "users" -> 78cVqzA8qNTNigsao3VvdnM0Qol2 (Which is correct) -> (several data pairs such as level : 1, lives : 3, etc)
public static async void GetUserData()
{
FirebaseApp app = FirebaseApp.DefaultInstance;
app.SetEditorDatabaseUrl("https://narwhaltrivia.firebaseio.com/");
if (app.Options.DatabaseUrl != null) app.SetEditorDatabaseUrl(app.Options.DatabaseUrl);
DatabaseReference reference = Firebase.Database.FirebaseDatabase.DefaultInstance.RootReference;
loggedUserId = FirebaseAuth.DefaultInstance.CurrentUser.UserId;
await reference.Database.GetReference("users").Child(loggedUserId).GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.LogError("Error retrieving user data");
return;
}
if (task.IsCompleted)
{
DataSnapshot userSnapshot = task.Result;
loggedEmail = userSnapshot.Child("email").GetRawJsonValue();
loggedCurrentScore = userSnapshot.Child("currentScore").GetRawJsonValue();
loggedLevel = userSnapshot.Child("level").GetRawJsonValue();
loggedLives = userSnapshot.Child("lives").GetRawJsonValue();
loggedRound = userSnapshot.Child("round").GetRawJsonValue();
loggedTotalScore = userSnapshot.Child("totalScore").GetRawJsonValue();
return;
}
});
}

Preserving DataContext, long running operation within Iterating

Following actions needs to be performed:
get a view from the database of users that needs to recieve an e-mail
send this email
update the records of the usres with a timestamp the email was send.
This code does as described above but in the wrong order (rough sketch/needs refactoring):
public class MailMessageController
{
public static IEnumerable<Invitation> GetInvitations()
{
using (boDataContext context = new boDataContext())
{
// the table where the timestamps have to be set
Table<Computer> computer = context.GetTable<Computer>();
// the view that contains the users email addresses
Table<Invitation> report = context.GetTable<Invitation>();
// get the view from the database
IEnumerable<Invitation> items = report.AsEnumerable<Invitation>();
foreach (Invitation item in items)
{
// update the timestamp (not good, the e-mail hasn't been sent)
Computer c = computer.FirstOrDefault(
i => i.ComputerID == item.ComputerID
);
if (c != null)
{
c.DateInvited = DateTime.Now;
}
yield return item;
}
context.SubmitChanges(); // <-- this should commit the changes
}
}
I send e-mails from this collections via:
foreach(Invitation item in MailMessageController.GetInvitations())
{
if (SendMessage(item)) // <<-- bool if message was sent successfully
{
// here I want to update the database with the timestamp
}
}
So I need to update the database with a timestamp after the e-mail was send succesfully.
But it seems that I can't get around the fact that I need to create a context for every instance I need to update.
So this is more of a design issue. How can I get the view from the database, send emails and update the timestamp in the most fluent and less-cost manner?
How about updating the timestamps outside the loop?
var timeStamps = new List<Tuple<DateTime, int>>();
foreach(Invitation item in MailMessageController.GetInvitations())
{
if (SendMessage(item)) // <<-- bool if message was sent successfully
{
timeStamps.Add(new Tuple<DateTime, int>(DateTime.Now, item.ID);
}
}
UpdateTimeStamps(timeStamps);

Categories