How to update a field in document in a firestore - c#

Im trying to update a string field in specific document using Firebase Firestore for my android app but every method I see is while knowing the document refernce which I find difficult to find in my program.
Would like for some help for another method or help in finding the document refernce using a specific field value.
Thanks in advance.
(using C# btw)
private async Task<string> GetDocRefAsync(string userId)
{
Object obj = await FirestoreData.GetFirestore().Collection(DBConstants.FS_COLLECTION_USERS).
WhereEqualTo(DBConstants.FS_COLLECTION_USERS_USER_ID, userId).Get();
QuerySnapshot snapshot = (QuerySnapshot)obj;
if (snapshot.IsEmpty)
{
Log.Debug("UpdateGroupAsync", "userId: " + userId + " not found");
return null;
}
string docRef = "";
foreach (DocumentSnapshot item in snapshot.Documents)
{
//docRef = item.;
}
return docRef;
}
Firstly ive tried to find the document ref using this code but dont have a function to get the ref even after getting the correct document.
the fourth line from the bottom is where I couldnt find it.
database pic
this.groupCode = code;
string strUserRef = GetDocRefAsync(userRef).ToString();
DocumentReference docReference = database.Collection(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE).Document(strUserRef);
docReference.Update(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE, groupCode);

If you want to get the documents where a field has a given value, you can use a query. Then once the query returns, you can get documents IDs with the .Id field on each DocumentShapshot in the returned documents.
You will also need to add await for the returned value since it is an async method returning a Task<string> not returning a string.
private async Task<string> GetDocRefAsync(string userId) {
CollectionReference usersRef = FirestoreData.GetFirestore().Collection(DBConstants.FS_COLLECTION_USERS);
Query query = usersRef.WhereEqualTo(DBConstants.FS_COLLECTION_USERS_USER_ID, userId);
// or GetSnapshotAsync depending on the version of firebase
QuerySnapshot querySnapshot = await query.Get();
// Note: if it matches multiple documents this will just return
// the ID of the first match
foreach (DocumentSnapshot item in snapshot.Documents)
{
return item.Id;
}
Log.Debug("UpdateGroupAsync", "userId: " + userId + " not found");
return null;
}
And you can use it like this to update a document (note that you were using a different collection here - probably by mistake).
string userDocId = await GetDocRefAsync(userId);
CollectionReference userCollection = database.Collection(DBConstants.FS_COLLECTION_USERS);
DocumentReference docReference = userCollection.Document(userDocId);
// or UpdateAsync depending on your version of firebase
docReference.Update(DBConstants.FS_COLLECTION_USERS_GROUPS_CODE, groupCode);

Related

C# MSGraph SDK and ipNamedLocation

I've been working on C# app to amend the ipaddress/s of a Named Location in conditional access in AAD.
I can authenticate and return the request collection. For whatever reason I cant access the isTrusted property or the ipRanges odata.
I can see the properties and the vales when I run through in debug, but cant output them.
I think its something to do with the list type, I'm using Microsoft.Graph.NamedLocation, there is Microsoft.Graph.IpNamedLocation type available but it can be converted from Microsoft.Graph.NamedLocation, which the api call makes.
The image shows what's available during runtime.
Code Below:
private static async Task GetnamedLocations(IConfidentialClientApplication app, string[] scopes)
{
GraphServiceClient graphServiceClient = GetAuthenticatedGraphClient(app, scopes);
var namedlocationsList = new List<Microsoft.Graph.NamedLocation>();
var namedLocations = await graphServiceClient.Identity.ConditionalAccess.NamedLocations
.Request()
.Filter("isof('microsoft.graph.ipNamedLocation')")
.GetAsync();
// var ipNamedLocations = new List<Microsoft.Graph.IpNamedLocation>();
namedlocationsList.AddRange(namedLocations.CurrentPage);
foreach (var namedLocation in namedlocationsList)
{
Console.WriteLine(namedLocation.Id + namedLocation.DisplayName + namedLocation.ODataType + namedLocation);
if (namedLocation.ODataType == "#microsoft.graph.ipNamedLocation")
{
Console.WriteLine("Write out all the properties");
}
}
Console.WriteLine(($"Named location: {namedLocations}"));
}
Any pointers gratefully received, I'm not a C# developer so be gentle :-)
You need to cast namedLocation to IpNamedLocation type.
foreach (var namedLocation in namedlocationsList)
{
Console.WriteLine(namedLocation.Id + namedLocation.DisplayName + namedLocation.ODataType + namedLocation);
if (namedLocation is IpNamedLocation ipNamedLocation)
{
var isTrusted = ipNamedLocation.IsTrusted;
var ipRanges = ipNamedLocation.IpRanges;
if (ipRanges is IEnumerable<IPv4CidrRange> ipv4cidrRanges)
{
foreach(var ipv4cidrRange in ipv4cidrRanges)
{
Console.WriteLine($"{ipv4cidrRange.CidrAddress}");
}
}
Console.WriteLine("Write out all the properties");
}
}
I couldn't get the updated answer to work, it still didn't evaluate the if statement to true, after a bit of googling and trying different options, the following returns the IP address, not sure if its the right way to go about it but it works.
var ipv4CidrRanges = ipRanges.Cast<IPv4CidrRange>().ToList();
foreach (var ipv4CidrRange in ipv4CidrRanges)
{
Console.WriteLine(ipv4CidrRange.CidrAddress);
}
Many thanks to user2250152 who solved the first conundrum for me.

ReplaceOneAsync() immediately after InsertOneAsync() not always working, even when journaled

On a single-instance MongoDB server, even with the write concern on the client set to journaled, one in every couple of thousand documents isn't replacable immediately after inserting.
I was under the impression that once journaled, documents are immediately available for querying.
The code below inserts a document, then updates the DateModified property of the document and tries to update the document based on the document's Id and the old value of that property.
public class MyDocument
{
public BsonObjectId Id { get; set; }
public DateTime DateModified { get; set; }
}
static void Main(string[] args)
{
var r = Task.Run(MainAsync);
Console.WriteLine("Inserting documents... Press any key to exit.");
Console.ReadKey(intercept: true);
}
private static async Task MainAsync()
{
var client = new MongoClient("mongodb://localhost:27017");
var database = client.GetDatabase("updateInsertedDocuments");
var concern = new WriteConcern(journal: true);
var collection = database.GetCollection<MyDocument>("docs").WithWriteConcern(concern);
int errorCount = 0;
int totalCount = 0;
do
{
totalCount++;
// Create and insert the document
var document = new MyDocument
{
DateModified = DateTime.Now,
};
await collection.InsertOneAsync(document);
// Save and update the modified date
var oldDateModified = document.DateModified;
document.DateModified = DateTime.Now;
// Try to update the document by Id and the earlier DateModified
var result = await collection.ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, document);
if (result.ModifiedCount == 0)
{
Console.WriteLine($"Error {++errorCount}/{totalCount}: doc {document.Id} did not have DateModified {oldDateModified.ToString("yyyy-MM-dd HH:mm:ss.ffffff")}");
await DoesItExist(collection, document, oldDateModified);
}
}
while (true);
}
The code inserts at a rate of around 250 documents per second. One in around 1,000-15,000 calls to ReplaceOneAsync(d => d.Id == document.Id && d.DateModified == oldDateModified, ...) fails, as it returns a ModifiedCount of 0. The failure rate depends on whether we run a Debug or Release build and with debugger attached or not: more speed means more errors.
The code shown represents something that I can't really easily change. Of course I'd rather perform a series of Update.Set() calls, but that's not really an option right now. The InsertOneAsync() followed by a ReplaceOneAsync() is abstracted by some kind of repository pattern that updates entities by reference. The non-async counterparts of the methods display the same behavior.
A simple Thread.Sleep(100) between inserting and replacing mitigates the problem.
When the query fails, and we wait a while and then attempt to query the document again in the code below, it'll be found every time.
private static async Task DoesItExist(IMongoCollection<MyDocument> collection, MyDocument document, DateTime oldDateModified)
{
Thread.Sleep(500);
var fromDatabaseCursor = await collection.FindAsync(d => d.Id == document.Id && d.DateModified == oldDateModified);
var fromDatabaseDoc = await fromDatabaseCursor.FirstOrDefaultAsync();
if (fromDatabaseDoc != null)
{
Console.WriteLine("But it was found!");
}
else
{
Console.WriteLine("And wasn't found!");
}
}
Versions on which this occurs:
MongoDB Community Server 3.4.0, 3.4.1, 3.4.3, 3.4.4 and 3.4.10, all on WiredTiger storage engine
Server runs on Windows, other OSes as well
C# Mongo Driver 2.3.0 and 2.4.4
Is this an issue in MongoDB, or are we doing (or assuming) something wrong?
Or, the actual end goal, how can I ensure an insert is immediately retrievable by an update?
ReplaceOneAsync returns 0 if the new document is identical to the old one (because nothing changed).
It looks to me like if your test executes fast enough the various calls to DateTime.Now could return the same value, so it is possible that you are passing the exact same document to InsertOneAsync and ReplaceOneAsync.

How can I delete a OneNote page in C#?

I'm using the OneNote interop in C# to try and delete a page using the following block of code:
static string GetObjectId(string parentId, HierarchyScope scope, string objectName)
{
string xml;
onenote.GetHierarchy(parentId, scope, out xml);
var doc = XDocument.Parse(xml);
var nodeName = "";
switch (scope)
{
case (HierarchyScope.hsNotebooks): nodeName = "Notebook"; break;
case (HierarchyScope.hsPages): nodeName = "Page"; break;
case (HierarchyScope.hsSections): nodeName = "Section"; break;
default:
return null;
}
var node = doc.Descendants(ns + nodeName)
.Where(n => n.Attribute("name").Value == objectName)
.FirstOrDefault();
return node.Attribute("ID").Value;
}
static string DeletePage(string sectionId, string pageId, string pageName)
{
var pageId = GetObjectId(sectionId, HierarchyScope.hsPages, pageName);
onenote.DeleteHierarchy(pageId, DateTime.MinValue, true);
}
But each time I open OneNote the page remains.
I have been reading on the following page that this is the method I should be using however I don't appear to be having much luck: https://msdn.microsoft.com/en-us/library/office/gg649853(v=office.14).aspx
Can someone please point me in the right direction on how to delete a page in OneNote using the OneNote interop in C#.
This is an old question but since there's no answer... I have found that simply calling the functions as follows:
onenote.DeleteHierarchy(pageId)
works and will result in the file being sent to the recycle bin. If you want to fully delete the page, do the following:
onenote.DeleteHierarchy(pageId, deletePermanently:true)
Using the dateExpectedLastModified parameter is a bit tricky.
Per MSDN– (Optional) The date and time that you think the object you want to delete was last modified. If you pass a non-zero value for this parameter, OneNote proceeds with the update only if the value you pass matches the actual date and time the object was last modified. Passing a value for this parameter helps prevent accidentally overwriting edits users made since the last time the object was modified.

C# Async Function To Replace Mongo Document Does Not Work

I am currently running into an issue where I am unable to replace a Mongo document thats supposed to be updated with the following function:
public async void updateSelectedDocument(BsonDocument document)
{
var collection = mongoClient.GetDatabase("db").GetCollection<BsonDocument>("collection");
var id = document.GetElement("_id").ToString().Remove(0, 4);
var filter = Builders<BsonDocument>.Filter.Eq("_id", id);
var result = await collection.ReplaceOneAsync(filter, document, new UpdateOptions { IsUpsert = true });
}
There is no error to go off of and none of the variables are null. My connection to the mongo instance and collection works because I can perform a find and an insert. Please Let me know if you need additional information.

Parse.com - if key exists, update

Currently, I'm sending some data to Parse.com. All works well, however, I would like to add a row if it's a new user or update the current table if it's an old user.
So what I need to do is check if the current Facebook ID (the key I'm using) shows up anywhere in the fbid column, then update it if case may be.
How can I check if the key exists in the column?
Also, I'm using C#/Unity.
static void sendToParse()
{
ParseObject currentUser = new ParseObject("Game");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("Sent to Parse");
}
Okay, I figured it out.
First, I check which if there is any Facebook ID in the table that matches the current ID, then get the number of matches.
public static void getObjectID()
{
var query = ParseObject.GetQuery("IdealStunts")
.WhereEqualTo("fbid", FB.UserId);
query.FirstAsync().ContinueWith(t =>
{
ParseObject obj = t.Result;
objectID = obj.ObjectId;
Debug.LogError(objectID);
});
}
If there is any key matching the current Facebook ID, don't do anything. If there aren't, just add a new user.
public static void sendToParse()
{
if (count != 0)
{
Debug.LogError("Already exists");
}
else
{
ParseObject currentUser = new ParseObject("IdealStunts");
currentUser["name"] = fbname;
currentUser["email"] = fbemail;
currentUser["fbid"] = FB.UserId;
Task saveTask = currentUser.SaveAsync();
Debug.LogError("New User");
}
}
You will have to do a StartCoroutine for sendToParse, so getObjectID has time to look through the table.
It may be a crappy implementation, but it works.
What you need to do is create a query for the fbid. If the query returns an object, you update it. If not, you create a new.
I'm not proficient with C#, but here is an example in Objective-C:
PFQuery *query = [PFQuery queryWithClassName:#"Yourclass]; // Name of your class in Parse
query.cachePolicy = kPFCachePolicyNetworkOnly;
[query whereKey:#"fbid" equalTo:theFBid]; // Variable containing the fb id
NSArray *users = [query findObjects];
self.currentFacebookUser = [users lastObject]; // Array should contain only 1 object
if (self.currentFacebookUser) { // Might have to test for NULL, but probably not
// Update the object and save it
} else {
// Create a new object
}

Categories

Resources