How to update 2 separate nodes in Firebase Realtime Database - c#

I have problems with transactions using Firebase. I want to update 2 nodes (models and scenes) at the same time but it never updates my database.
Here is my code:
public static void Delete(DataContainers.Scene iScene)
{
DatabaseReference leaderBoardRef = FirebaseDatabase.DefaultInstance.RootReference;
leaderBoardRef.RunTransaction(mutableData =>
{
mutableData.Child("Scenes/" + iScene.id).Value = null;
foreach (var _model in iScene.models)
{
mutableData.Child("models/" + _model).Value = null;
}
return TransactionResult.Success(mutableData);
});
}
Is there any other way how to do it?

You can use a multi-path update to delete all the data at once like you desire:
private void deleteScene(DataContainers.Scene iScene) {
// Get root database reference
DatabaseReference mDatabase = FirebaseDatabase.DefaultInstance.RootReference;
// Initialize a new list of "path->value" pairs
Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
// Delete the given scene and all of its models
childUpdates["/Scenes/" + iScene.id] = null;
foreach (var _model in iScene.models)
{
childUpdates["/models/" + _model] = null;
}
mDatabase.UpdateChildrenAsync(childUpdates);
}

Related

List to Dictionary and back again

I have read many posts about dictionaries and serialization etc. and they all have different ways of doing things.
Being new to coding (to an extent) I am having trouble figuring out how to get my dictionary to a list or serialized to get it saved then reloaded on play. This is Unity BTW.
So here is what I am using.
I have an application manager that is mainly just a set of BOOL values that tells my mission system if something has been completed or not. Something like "GetWeapon FALSE" (then when they pick it up it changes to TRUE which tells the mission system to move to the next objective or complete)
So I have a starting list of keys,values...these then get placed into a dictionary and the values are changed within that.
I need to be able to save those values and reload them on LOAD (default PLAY mode is newgame--as you see below it resets the dictionary and copies in the starting states). I know it can't be as difficult as I am making it, just not understanding the whole serialize thing.
Most sites are showing a dictionary like {key,value} so I am getting lost on iterating through the dictionary and capturing the pairs and saving them.
Here is partial code for the appmanager (it is a singleton):
// This holds any states you wish set at game startup
[SerializeField] private List<GameState> _startingGameStates = new List<GameState>();
// Used to store the key/values pairs in the above list in a more efficient dictionary
private Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>();
void Awake()
{
// This object must live for the entire application
DontDestroyOnLoad(gameObject);
ResetGameStates();
}
void ResetGameStates()
{
_gameStateDictionary.Clear();
// Copy starting game states into game state dictionary
for (int i = 0; i < _startingGameStates.Count; i++)
{
GameState gs = _startingGameStates[i];
_gameStateDictionary[gs.Key] = gs.Value;
}
OnStateChanged.Invoke();
}
public GameState GetStartingGameState(string key)
{
for (int i = 0; i < _startingGameStates.Count; i++)
{
if (_startingGameStates[i] != null && _startingGameStates[i].Key.Equals(key))
return _startingGameStates[i];
}
return null;
}
// Name : SetGameState
// Desc : Sets a Game State
public bool SetGameState(string key, string value)
{
if (key == null || value == null) return false;
_gameStateDictionary[key] = value;
OnStateChanged.Invoke();
return true;
}
Tried something similar to this:
Dictionary<string, string> _gameStateDictionary = new Dictionary<string, string>
{
for (int i = 0; i < _gameStateDictionary.Count; i++)
string json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
Debug.Log(json);
{
}
However all I got was the first item in the list. (I did modify the above in a for loop) I know the above is wrong, I did other iterations to just to get the dictionary to print out in the console.
Anyway, just asking for a little code help to save and load a dictionary.
Since you want to save and load those persistent anyway, personally I would not use the Inspector and bother with Unity serialization at all but rather directly serialize/deserialize from and to json.
Simply place your default dictionary as a simple json file like e.g.
{
"Key1" : "Value1",
"Key2" : "Value2",
"Key3" : "Value3",
...
}
within the folder Assets/StreamingAssets (create it if it doesn't exists). This will be your default fallback file if none exists so far.
On runtime you will load it from Application.streamingAssetsPath.
Then when saving you will not put it there but rather to the Application.persistentDataPath
using Newtonsoft.Json;
...
[SerializeField] private string fileName = "example.json";
private string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
private Dictionary<string, string> _gameStateDictionary
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void Save()
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.intended);
File.WriteAllText(filePath, json);
}
If you then still want to edit the default via the Inspector you could merge both and instead of using StreamingAssets do
[SerializeField] private GameState[] defaultStates;
public void Load()
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath))
{
LoadDefaults();
return;
}
var json = File.ReadAllText(filePath);
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
public void LoadDefaults()
{
_gameStateDictionary = defaultStates.ToDictionary(gameState => gameState.Key, gameState => gameState.Value);
}
And then btw you could do
public string GetState(string key, string fallbackValue = "")
{
if(_gameStateDictionary.TryGetValue(key, out var value))
{
return value;
}
return fallbackValue;
}
You can iterate the dictionary by using its Keys collection.
foreach (var key in myDictionary.Keys)
{
var val = myDictionary[key];
}
Populate your data structure with the key/value pairs and serialize the list of items.
var missionItems = new List<MissionItem>();
foreach (var key in myDictionary.Keys)
{
var missionItem = new MissionItem();
missionItem.Key = key;
missionItem.Value = myDictionary[key];
missionItems.Add(missionItem);
}
var json = JsonUtility.ToJson(missionItems);
Loading is straight forward, deserialize your data, then loop through all the items and add each one to the empty dicitionary.
var missionItems = JsonUtility.FromJson<List<MissionItem>>(json);
foreach (var item in missionItems)
{
myDictionary.Add(item.Key, item.Value);
}
Here is my final setup.
I call these on button press in another script (the UI pause menu save/load buttons)
private bool useEncryption = true;
private string encryptionCodeWord = "CodeWordHere";
private static string fileName = "name.json";
private static string defaultStatesPath => Path.Combine(Application.streamingAssetsPath, fileName);
private static string persistentStatesPath => Path.Combine(Application.persistentDataPath, fileName);
public void SaveStates(int slotIndex)
{
var filePath = persistentStatesPath;
if(!Directory.Exists(Application.persistentDataPath))
{
Directory.CreateDirectory(Application.persistentDataPath);
}
var json = JsonConvert.SerializeObject(_gameStateDictionary, Formatting.Indented);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
File.WriteAllText(filePath + slotIndex, json);
}
public void LoadStates(int SlotIndex)
{
var filePath = persistentStatesPath;
if(!File.Exists(filePath + SlotIndex))
{
filePath = defaultStatesPath;
}
var json = File.ReadAllText(filePath + SlotIndex);
if (useEncryption)
{
Debug.Log("Using Encryption");
json = EncryptDecrypt(json);
}
_gameStateDictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
}
private string EncryptDecrypt(string data)
{
string modifiedData = "";
for (int i = 0; i < data.Length; i++)
{
modifiedData += (char)(data[i] ^ encryptionCodeWord[i % encryptionCodeWord.Length]);
}
return modifiedData;
}

Can't update SalesReceipt in Quickbooks desktop

I'm using QuickBooks Integrator from /nSoftware to integrate with QuickBooks Desktop
I'm trying to update an invoice and I don't get any errors but when I check in QuickBooks I see that nothing changed and it didn't actually get updated.
First I try to lookup the invoice based on the RefNumber and if it found an Invoice then I try to replace the Line Items and then i call the update method like this existingInvoice.Update();
Here's my code sample:
public static List<Invoice> FindInvoice(string refNumber)
{
var invoicesSearch = new Objsearch
{
QueryType = ObjsearchQueryTypes.qtInvoiceSearch,
RuntimeLicense = "MYLICENSEKEY",
QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR",
SearchCriteria = new SearchCriteria
{
RefNumberContains = refNumber
},
};
invoicesSearch.Search();
var qbInvoiceList = invoicesSearch.Results.ToList();
var invoiceObjList = new List<Invoice>();
foreach (var inv in qbInvoiceList)
{
var newInv = new Invoice();
newInv.QBResponseAggregate = inv.Aggregate;
invoiceObjList.Add(newInv);
}
return invoiceObjList.FirstOrDefault();
}
public static void PutInvoice(Invoice invoice)
{
var existingInvoice = FindInvoice(invoice.RefNumber);
if (existingInvoice != null)
{
existingInvoice.LineItems.Clear();
existingInvoice.LineItems.AddRange(invoice.LineItems);
existingInvoice.QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR";
existingInvoice.RuntimeLicense = RuntimeLicense;
existingInvoice.QBXMLVersion = "12.0";
existingInvoice.Update(); //this line
}
}
Okay, so the issue was that I was setting the QBXMLVersion the last thing before updating.
In order for the Update() to process successfully the QBXMLVersion needs to be set the first thing.
Here's an updated working example:
public static void PutInvoice(Invoice invoice)
{
var existingInvoice = FindInvoice(invoice.RefNumber);
if (existingInvoice != null)
{
existingInvoice.QBXMLVersion = "12.0";
existingInvoice.RuntimeLicense = "MyRuntimeLicenseKey";
existingInvoice.QBConnectionString = "MYCONNECTIONSTRINGTOREMOTECONNECTOR";
existingInvoice.LineItems.Clear();
existingInvoice.LineItems.AddRange(invoice.LineItems);
existingInvoice.Update();
}
}

C# Method is not executing sequentially

I have the below code:
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
}
}
ticketStoreNew = ticketStore;
return output;
}`
It takes a dictionary, ticketStore, checks to see if it has any new elements not in ticketStoreNew and puts them in the output dictionary. Then, ticketStoreNew is set to ticketStore until ticketStore is updated with another method and this method is ran again.
However, when I include the line ticketStoreNew = ticketStore, the program returns an empty dictionary. It looks like the method is not executing sequentially and this is running prior to the for loop.
I really just need to return any new items added to the ticketStore dictionary.
EDIT
Below is the code for getting ticketStore:
public void UpdateTickets(string inputXml)
{
// If no new tickets exit
if (inputXml.Trim() == "") { return; }
//xmlString = inputXml;
// Load XML into an enumerable
XElement xelement = XElement.Parse(inputXml);
IEnumerable<XElement> xml = xelement.Elements();
foreach (var item in xml)
{
if (item.Name == "incident")
{
int id;
// If ID can be converted to INT
if (Int32.TryParse(item.Element("id").Value, out id))
{
// If ticket is not already in store create ticket and populate data
if (!ticketStore.ContainsKey(id))
{
Ticket ticket = new Ticket();
ticket.id = id;
ticket.number = Int32.Parse(item.Element("number").Value);
ticket.title = item.Element("name").Value;
ticket.description = item.Element("description").Value;
ticketStore.Add(id, ticket);
}
}
}
}
}
}
The tickets are all based on getting XML from the Samanage API.
If another method updates ticketStore then the assignment is the problem. It doesn't copy the contents of ticketStore to ticketStoreNew it sets the reference ticketStoreNew to point to the same instance as ticketStore. Thus they are the same object and always have the same contents. Try creating a new Dictionary to copy the items:
ticketStoreNew = new Dictionary<int, Ticket>(ticketStore);
Try this code:
private Dictionary<int, Ticket> ticketStoreNew =
new Dictionary<int, Ticket>(); // add this line
public Dictionary<int, Ticket> GetNewTickets()
{
Dictionary<int, Ticket> output = new Dictionary<int, Ticket>();
foreach (KeyValuePair<int, Ticket> item in ticketStore)
{
if (!ticketStoreNew.ContainsKey(item.Key))
{
output.Add(item.Key, item.Value);
ticketStoreNew.Add(item.Key, item.Value); // add this line
}
}
//ticketStoreNew = ticketStore; remove this line
return output;
}

Bing API Query Callback to Observable Collection

Created a Windows Store App using some info found on this post: How do I use the Bing Search API in Windows Phone?
Goal
Textbox - Type any term
Search Button - Searches that term and populates a GridView of pictures retrieved with the Bing API
Problem
I get the pictures, and they are received via my "OnQueryComplete" callback, but I can't figure out what the correct way to populate the collection would be. Since I can't figure out how to await this call, I (just to see if I could get it working, which it does) added a while loop (which you can probably see the issues with). What would be the correct way to do this? How do you handle callbacks for populating the GridView and having it wait until it's finished?
Current ViewModel Code
public bool itemsFinished = false;
private ObservableCollection<SearchResult> _ImageResults;
public ObservableCollection<SearchResult> ImageResults {
get {
if (_ImageResults == null) {
while (!itemsFinished) {
int i = 0;
i++;
}
}
return _ImageResults;
}
set {
_ImageResults = value;
}
}
public SearchResultViewModel() {
GetPictures("dogs");
}
public void GetPictures(string searchTerm) {
// This is the query - or you could get it from args.
string query = searchTerm;
// Create a Bing container.
string rootUri = "https://api.datamarket.azure.com/Bing/Search";
var bingContainer = new Bing.BingSearchContainer(new Uri(rootUri));
// Replace this value with your account key.
var accountKey = "myaccountkey";
// Configure bingContainer to use your credentials.
bingContainer.Credentials = new NetworkCredential(accountKey, accountKey);
// Build the query.
var imageQuery = bingContainer.Image(query, null, null, null, null, null, null);
imageQuery.BeginExecute(OnQueryComplete, imageQuery);
// var imageResults = imageQuery.Execute();
}
private void OnQueryComplete(IAsyncResult result) {
// ImageResults.Clear();
_ImageResults = new ObservableCollection<SearchResult>();
var query = (DataServiceQuery<ImageResult>)result.AsyncState;
var enumerableResults = query.EndExecute(result);
int i = 0;
foreach (var item in enumerableResults) {
SearchResult myResult = new SearchResult();
myResult.Title = item.Title;
myResult.ImageUri = new Uri(item.MediaUrl);
ImageResults.Add(myResult);
i++;
if (i >= 14) {
break;
}
}
itemsFinished = true;
}
Please excuse any syntax errors, I'm without a Visual Studio instance right now.
The problem I see is that you reset your ObservableCollection when you receive content.
Try it as follows:
private ObservableCollection<SearchResult> _ImageResults;
public ObservableCollection<SearchResult> ImageResults {
get
{
return _ImageResults;
}
set {
_ImageResults = value;
}
}
public SearchResultViewModel() {
_ImageResults = new ObservableCollection<SearchResult>(); // Just create it once.
GetPictures("dogs");
}
private void OnQueryComplete(IAsyncResult result) {
_ImageResults.Clear(); // Clear isn't bad, that way you keep your reference to your original collection!
//_ImageResults = new ObservableCollection<SearchResult>(); // We already have one. ObservableCollection works best if you keep on working with the collection you have.
var query = (DataServiceQuery<ImageResult>)result.AsyncState;
var enumerableResults = query.EndExecute(result);
int i = 0;
foreach (var item in enumerableResults) {
SearchResult myResult = new SearchResult();
myResult.Title = item.Title;
myResult.ImageUri = new Uri(item.MediaUrl);
ImageResults.Add(myResult);
i++;
if (i >= 14) {
break;
}
}
}
As far as I see (can't test sadly) this should work, provided you have bound your ObservableCollection the right way in your xaml.

How can I pass a function with one parameter to an ICommand?

Here's my ICommand:
public ICommand ConfirmLotSavedCommand {
get
{
return new RelayCommand(ConfirmLotSaved);
}
}
The problem is I have deserialized data that I want to store into database after a user clicks confirm button. If the user does not click on confirm or the lot number already exists, then I don't want to save the deserialized string in db.
I had trouble calling a function with one parameter inside my ConfirmLotSaved() method because of scope.
So I created a set the deserialized lot as a field and put the code to save to db inside of ConfirmLotSaved(). However, the field is null for some strange reason... I'm not sure why.
Here's my attempt:
private LotInformation lot; //field that is supposed to contain all the deserialized info
private void ConfirmLotSaved()
{
using (var db = new DDataContext())
{
bool lotNumDbExists = db.LotInformation.Any(r => r.lot_number == DeserialLotNumber);
if (lotNumDbExists == false)
{
successWindow.Message = "Successfully Saved Lot";
dialogService.ShowDialog(successWindow.Message, successWindow);
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = Lot.lot_number;
ExpirationDate = Lot.exp_date.ToString();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
else if (lotNumDbExists == true)
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
}
Deserialization function to see where lot is grabbing data:
public void DeserializedStream(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
LotInformation lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
LotInformation newList = new LotInformation();
using (var db = new DDataContext())
{
bool isDuplicate = db.LotInformation.Any(r => r.lot_number == lot.lot_number);
if (newList != null && isDuplicate == false)
{
newList.Id = lot.Id;
newList.lot_number = lot.lot_number;
newList.exp_date = lot.exp_date;
DeserialLotNumber = newList.lot_number;
DeserialExpirationDate = newList.exp_date.ToString();
foreach (Component comp in lot.Components)
{
newList.Components.Add(comp);
}
DeserialComponentsList = newList.Components;
foreach (Families fam in lot.Families)
{
newList.Families.Add(fam);
}
DeserialFamiliesList = newList.Families;
}
else if (isDuplicate == true)
{
DeserialAnalytesList = null;
DeserialFamiliesList = null;
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
return;
}
}
}
I figured out what was wrong:
After setting private LotInformation lot; field before constructor, I redeclared locally my mistake:
LotInformation lot = new LotInformation();
Changed it to:
lot = new LotInformation();
and it works.
I suggest you to use RelayCommand's generic edition http://www.kellydun.com/wpf-relaycommand-with-parameter/
It will allow you to pass lot to your command from view, all you need to store lot in current DataContext.

Categories