How to check in MongoDB C# Driver if updated successfully? - c#

I am using the following code to update data using the MongoDB C# Driver:
public async Task<bool> UpdateFirstName(string id, string firstName)
{
await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
}
This method returns "bool", because I want to know if the data has been updated successfully. This would be the pseudocode for checking if the data has been updated successfully:
if (data updated successfully)
{
return true;
}
else
{
return false;
}
Does anyone know how to write the code for checking if the data updated successfully? Thanks!

If the method was executed so the update was done, otherwise the method will throw an exception - In case of async it's important to not forget the await (since using async method without await can't ensure your application stay around long enough to retreive the exception)

UpdateOneAsync has a return value of UpdateResult?, which gives you access to the ModifiedCount. As you you UpdateOne a single greater 0 check is enough.
var response = await Collection.UpdateOneAsync(Builders<User>.Filter.Eq(
"_id", new ObjectId(id)),
Builders<User>.Update.Set("firstName", firstName)
.CurrentDate("lastUpdatedDate"));
if response.ModifiedCount > 0 {
// success
}
// failed
This will throw an Exception if the Update is not acknowledged.

Related

Adding more detail to await Task<T> in C# .net

The Task object created in C# contains the details for the completion of the Action ascribed to the Task. So even if the Action fails the task is still IsCompleted, unless the Task throws an exception or is cancelled.
What I am trying to do is to create a Task-Like class (easy, done that bit) that can store more information about the job that was done.
So for example I want an API to get some accounts, the results may throw an exception, say SQLConnection Exception for example, fine, but also the results may be empty and I want to know why. Is the Database empty or has a filter restricted the view to nothing, for example?
So I could do:
var t = AccountResposistory.GetAccountsAsync(filter, token);
var results = await t;
if(t.Success)
{
return Results.Ok(results)
}
else
{
return Results.Problem(t.ErrorMessage);
}
I was heading down the path of using a custom Task-Like object something like:
public async ServiceTask<IEnumerable<Account>?> GetAccountsAsync(Filter filter, CancellationToken token)
{
// Get accounts with filtering
if(Accounts.Any())
{
return Accounts;
}
else if(filter != null)
{
return ServiceTask.FromError("Filter has restricted all Accounts");
}
else
{
return ServiceTask.FromError("Database doesn't contain any Accounts");
}
}
I can have ServiceTask.FromError return a default T but there's no way (I can see) to access the ServiceTask that's returned by the method to add in the details.
Alternatively, I figured I could have ServiceTask always return a generic Response class and then work with the properties inside ServiceTask to apply the results or messages, but I can't figure out how to do that, how to restrict ServiceTask that T is always a generic class of ServiceResponse
I don't like the idea of throwing exceptions when an exception hasn't happened. It's not a code exception when the database is empty, or the filter has removed all accounts.
Currently, my GetAccountsAsync is returning a Task<Response> and the Response has Success, ErrorMessage and Results properties. It becomes a bit cumbersome to work around this, to get the results of the awaited results. I'm hoping there's a simple way to code this.

Retrieve Data from Realtime Database with Action Callback

I am trying to receive the JSON value from the Realtime Database of Firebase using Unity.
I do the following:
FirebaseDatabase.DefaultInstance
.GetReference("Leaders").OrderByChild("score").GetValueAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
Debug.LogError("error in reading LeaderBoard");
return;
}
else if (task.IsCompleted)
{
Debug.Log("Received values for Leaders.");
string JsonLeaderBaord = task.Result.GetRawJsonValue();
callback(JsonLeaderBaord);
}
}
});
Trying to Read the CallBack :
private string GetStoredHighScores()
{
private string JsonLeaderBoardResult;
DataBaseModel.Instance.RetriveLeaderBoard(result =>
{
JsonLeaderBoardResult = result; //gets the data
});
return JsonLeaderBoardResult; //returns Null since it doesn't wait for the result to come.
}
Question is how do i wait for the callback to return the value and afterwards return the value of the JsonLeaderBoardResult.
return JsonLeaderBoardResult; //returns Null since it doesn't wait
for the result to come.
The RetriveLeaderBoard function doesn't return immediately. You can either use coroutine to wait for it or return the JsonLeaderBoardResult result via Action. Using Action make more sense in your case.
Change the string return type to void then return the result through Action:
private void GetStoredHighScores(Action<string> callback)
{
string JsonLeaderBoardResult;
DataBaseModel.Instance.RetriveLeaderBoard(result =>
{
JsonLeaderBoardResult = result; //gets the data
if (callback != null)
callback(JsonLeaderBoardResult);
});
}
Usage:
GetStoredHighScores((result) =>
{
Debug.Log(result);
});
EDIT:
That is great, but still need to do some stuff after getting the
result in `GetStoredHighScores' outside the Action, otherwise i can
get an error like: get_transform can only be called from the main
thread.
You get this error because RetriveLeaderBoard is running from on another Thread. Grab UnityThread from this post then do the callback on the main Thread with UnityThread.executeInUpdate.
Your new code:
void Awake()
{
UnityThread.initUnityThread();
}
private void GetStoredHighScores(Action<string> callback)
{
string JsonLeaderBoardResult;
DataBaseModel.Instance.RetriveLeaderBoard(result =>
{
JsonLeaderBoardResult = result; //gets the data
UnityThread.executeInUpdate(() =>
{
if (callback != null)
callback(JsonLeaderBoardResult);
});
});
}
You're seeing a classical confusion with asynchronous APIs. Since loading data from Firebase may take some time, it happens asynchronously (on a separate thread). This allows your main code to continue, while the Firebase client is waiting for a response from the server. When the data is available, the Firebase client calls your callback method with that data, so that you can process it.
It's easiest to see what this does in your code by placing a few logging statements:
Debug.Log("Before starting to load data");
FirebaseDatabase.DefaultInstance
.GetReference("Leaders").OrderByChild("score").GetValueAsync().ContinueWith(task => {
Debug.Log("Got data");
}
});
Debug.Log("After starting to load data");
When you run this code, it prints:
Before starting to load data
After starting to load data
Got data
This is probably not the order in which you expected the output. Due to the asynchronous nature of the call to Firebase, the second log line gets printed last. That explains precisely why you're seeing an empty array when you return it: by that time the data hasn't been loaded from Firebase yet, and your ContinueWith hasn't executed yet.
The solution to this is always the same: since you can't return data that hasn't loaded yet, you have to implement a callback function. The code you shared does that twice already: once for the ContinueWith of Firebase itself and one in RetriveLeaderBoard.
Any code that needs to have the up to date leaderboard, will essentially have to call RetriveLeaderBoard and do any work with the leaderboard data in its callback. For example, if you want to print the leaderboard:
DataBaseModel.Instance.RetriveLeaderBoard(result => {
Debug.Log(result);
});

If another request is running a method wait until finish

I'm developing an ASP.NET Web API application with C#, .NET Framework 4.7 and MongoDb.
I have this method:
[HttpPut]
[Route("api/Public/SendCommissioning/{serial}/{withChildren}")]
public HttpResponseMessage SendCommissioning(string serial, bool withChildren)
{
string errorMsg = "Cannot set commissioning.";
HttpResponseMessage response = null;
bool serverFound = true;
try
{
[...]
// Mongo
MongoHelper mgHelper = new MongoHelper();
mgHelper.InsertCommissioning(serial, withChildren);
}
catch (Exception ex)
{
_log.Error(ex.Message);
response = Request.CreateResponse(HttpStatusCode.InternalServerError);
response.ReasonPhrase = errorMsg;
}
return response;
}
Sometimes this method is called very quickly and I get an error here:
// Mongo
MongoHelper mgHelper = new MongoHelper();
mgHelper.InsertCommissioning(serial, withChildren);
Here I'm inserting the serials I received in order, and sometimes I get an error with a duplicated key in MongoDb:
I have a method to get the latest id used in Mongo (the primary key). And two requests get the same id, so when I try to insert it on Mongo I get an invalid key exception.
I thought to use a queue to store the serials and then consume them in the same order that I have received them. But I think I will get the same error when I try to store the serial in MongoDb.
Maybe if I can set a method that if it is running, I have to wait to run it, it will works. This method will have the part of insert the serials into Mongo.
How can I do that? A method that if it is running you can't run it in another Web Api request.
Or, do you know a better option?
By the way, I can't block this method. Maybe I need to run a thread with this synchronized part.

Easy tables with Xamarin Forms - InvalidOperationException

I am using this tutorial in order to connect a xamarin.forms app with easy tables. I cannot add data to the database in Azure as i get
System.InvalidOperationException
The error message is the following
An insert operation on the item is already in the queue.
The exception happends in the following line of code.
await usersTable.InsertAsync(data);
In order to add a user
var user = new User { Username = "username", Password = "password" };
bool x = await AddUser(user);
AddUser
public async Task<bool> AddUser(User user)
{
try
{
await usersTable.InsertAsync(user);
await SyncUsers();
return true;
}
catch (Exception x)
{
await new MessageDialog(x.Message.ToString()).ShowAsync();
return false;
}
}
SyncUsers()
public async Task SyncUsers()
{
await usersTable.PullAsync("users", usersTable.CreateQuery());
await client.SyncContext.PushAsync();
}
where
IMobileServiceSyncTable<User> usersTable;
MobileServiceClient client = new MobileServiceClient("url");
Initialize
var path = Path.Combine(MobileServiceClient.DefaultDatabasePath, "DBNAME.db");
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<User>();
await client.SyncContext.InitializeAsync(store, new MobileServiceSyncHandler());
usersTable = client.GetSyncTable<User>();
Please check your table. You probably have added the item already. Also, I would suggest that you don't set the Id property for your entity, because you might be inserting a same ID that's already existing in your table. It's probably the reason why the exception is appearing.
Hope it helps!
Some debugging you can do:
1) Turn on diagnostic logging in the backend and debug the backend: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter8/developing/#debugging-your-cloud-mobile-backend
2) Add a logging delegating handler in your MobileServiceClient setup: https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter3/server/#turning-on-diagnostic-logs
The MobileServicePushFailedException contains an inner exception that contains the actual error. Normally, it is one of the 409/412 HTTP errors, which indicates a conflict. However, it can also be a 404 (which means there is a mismatch between what your client is asking for and the table name in Easy Tables) or 500 (which means the server crashed, in which case the server-side diagnostic logs indicate why).
Easy Tables is just a Node.js service underneath the covers.

Update UI using partial view in response of Task Run (Async)

I am working on MVC C# Razor Framework 4.6.
I have static method ExportManager.ExportExcelCannedReportPPR wrapped up in Task.Run for long running report. This method returns boolean value and based on that I am refreshing partial view (_NotificationPanel).
public ActionResult ExportCannedReport(string cannedReportKey, string cannedReportName)
{
string memberKeys = _curUser.SecurityInfo.AccessibleFacilities_MemberKeys; //ToDo: Make sure this is fine or need to pass just self member?
string memberIds = _curUser.SecurityInfo.AccessibleFacilities_MemberIDs; //ToDo: Make sure this is fine or need to pass just self member?
string curMemberNameFormatted = _curUser.FacilityInfo.FacilityName.Replace(" ", string.Empty);
string cannedReportNameFormatted = cannedReportName.Replace(" ", string.Empty);
string fileName = string.Concat(cannedReportNameFormatted, "_", DateTime.Now.ToString("yyyyMMdd"), "_", curMemberNameFormatted);
//ToDo: Make sure below getting userId is correct
string userId = ((_curUser.IsECRIStaff.HasValue && _curUser.IsECRIStaff.Value) ? _curUser.MembersiteUsername : _curUser.PGUserName);
var returnTask = Task.Run<bool>(() => ExportManager.ExportExcelCannedReportPPR(cannedReportKey, cannedReportName, fileName, memberIds, userId));
returnTask.ContinueWith((antecedent) =>
{
if (antecedent.Result == true)
{
return PartialView("_NotificationPanel", "New file(s) added in 'Download Manager'.");
}
else
{
return PartialView("_NotificationPanel", "An error occurred while generating the report.");
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
return PartialView("_NotificationPanel", "");
}
Now issue is that UI could not get refresh even though _NotificationPanel in ContinueWith get executed.
The issue is that once you return from it - that request is done. You cannot return from it multiple times for a single request. The request and response are a 1-to-1. You need to use async and await here, such that when the export is done then and only then return a result.
public async Task<ActionResult> ExportCannedReport(string cannedReportKey,
string cannedReportName)
{
// Omitted for brevity...
var result =
await Task.Run<bool>(() =>
ExportManager.ExportExcelCannedReportPPR(cannedReportKey,
cannedReportName,
fileName,
memberIds,
userId));
return PartialView("_NotificationPanel",
result
? "New file(s) added in 'Download Manager'."
: "An error occurred while generating the report.");
}
You need to make the method Task returning such that it is "awaitable". Then you mark the method as async which enables the await keyword. Finally, you're ready to execute the long running task and from the result correctly determine and return the desired partial view update.
Update
Alternatively, you could utilize an AJAX call on the client and update once the server responds. For details on that specifically checkout MSDN.

Categories

Resources