in firestore/testapp/Assets/Firebase/Sample/Firestore/UIHandler.cs [399:506]
private IEnumerator PerformTransaction() {
DocumentReference doc1 = db.Collection("col3").Document("txn_doc1");
DocumentReference doc2 = db.Collection("col3").Document("txn_doc2");
DocumentReference doc3 = db.Collection("col3").Document("txn_doc3");
// Initialize doc1 and doc2 with some data.
var initialData = new Dictionary<string, object>{
{"field", "value"},
};
yield return new WaitForTaskCompletion(this, doc1.SetAsync(initialData));
yield return new WaitForTaskCompletion(this, doc2.SetAsync(initialData));
// Perform transaction that reads doc1, deletes doc1, updates doc2, and overwrites doc3.
DebugLog("INFO: Going to perform the following three operations in a transaction:");
DebugLog("\tDelete col3/txn_doc1");
DebugLog("\tUpdate col3/txn_doc2");
DebugLog("\tOverwrite col3/txn_doc3");
var txnTask = doc1.Firestore.RunTransactionAsync<string>((transaction) => {
return transaction.GetSnapshotAsync(doc1).ContinueWith((getTask) => {
transaction.Delete(doc1);
transaction.Update(doc2, new Dictionary<string, object> { { "field2", "value2" } });
transaction.Update(doc2, new Dictionary<FieldPath, object> { { new FieldPath("field3"), "value3" } });
transaction.Update(doc2, "field4", "value4");
transaction.Set(doc3, initialData);
// Since RunTransactionAsync<string> is used, we can return a string here, which can be
// accessed via Task.Result when the task is completed.
return "SUCCESS";
});
});
yield return new WaitForTaskCompletion(this, txnTask);
if (txnTask.IsCanceled) {
DebugLog("INFO: Transaction operation was cancelled.");
} else if (txnTask.IsFaulted) {
DebugLog("ERROR: An error occurred while performing the transaction.");
DebugLog("ERROR: " + txnTask.Exception.ToString());
} else {
string result = txnTask.Result;
DebugLog("INFO: Transaction completed with status: " + result);
}
if (!(txnTask.IsFaulted || txnTask.IsCanceled)) {
DebugLog("INFO: Checking the resulting documents.");
Task<DocumentSnapshot> get1 = doc1.GetSnapshotAsync();
yield return new WaitForTaskCompletion(this, get1);
if (get1.IsCanceled) {
DebugLog("INFO: Read operation for txn_doc1 was cancelled.");
} else if (get1.IsFaulted) {
DebugLog("ERROR: An error occurred while retrieving col3/txn_doc1.");
DebugLog("ERROR: " + get1.Exception.ToString());
} else {
DocumentSnapshot snap = get1.Result;
if(snap.Exists) {
DebugLog("ERROR: col3/txn_doc1 should have been deleted.");
} else {
DebugLog("Success: col3/txn_doc1 does not exist.");
}
}
Task<DocumentSnapshot> get2 = doc2.GetSnapshotAsync();
yield return new WaitForTaskCompletion(this, get2);
if (get2.IsCanceled) {
DebugLog("INFO: Read operation for txn_doc2 was cancelled.");
} else if (get2.IsFaulted) {
DebugLog("ERROR: An error occurred while retrieving col3/txn_doc2.");
DebugLog("ERROR: " + get2.Exception.ToString());
} else {
DocumentSnapshot snap = get2.Result;
if(snap.Exists) {
bool deepEquals = ObjectDeepEquals(snap.ToDictionary(),
new Dictionary<string, object> {
{ "field", "value" },
{ "field2", "value2" },
{ "field3", "value3" },
{ "field4", "value4" },
});
if(deepEquals) {
DebugLog("Success: col3/txn_doc2 content is as expected.");
} else {
DebugLog("ERROR: col3/txn_doc2 has incorrect content.");
}
} else {
DebugLog("ERROR: col3/txn_doc2 does not exist.");
}
}
Task<DocumentSnapshot> get3 = doc3.GetSnapshotAsync();
yield return new WaitForTaskCompletion(this, get3);
if (get3.IsCanceled) {
DebugLog("INFO: Read operation for txn_doc3 was cancelled.");
} else if (get3.IsFaulted) {
DebugLog("ERROR: An error occurred while retrieving col3/txn_doc3");
DebugLog("ERROR: " + get3.Exception.ToString());
} else {
DocumentSnapshot snap = get3.Result;
if(snap.Exists) {
bool deepEquals = ObjectDeepEquals(snap.ToDictionary(), initialData);
if(deepEquals) {
DebugLog("Success: col3/txn_doc3 content is as expected.");
} else {
DebugLog("ERROR: col3/txn_doc3 has incorrect content.");
}
} else {
DebugLog("ERROR: col3/txn_doc3 does not exist.");
}
}
}
}