If another request is running a method wait until finish - c#

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.

Related

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.

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

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.

OData Simple Client: Linked collection for type [Type] not found

I have xamarin forms application. I use odata simple client to manipulate app database.
I am trying to add data to many-to-many tables.
This is my first entity
public class Genre : BaseGenre
{
List<User> Users { get; set; }
}
And my other one
public class User : BaseUser
{
List<Genre> Genres { get; set; }
}
And this the function I am trying to link them
public async void AddGenresAsnyc(User u, List<Genre> Genres)
{
u.Genres = Genres;
try {
//await client.For<User> (CollectionName).Key(u).LinkEntryAsync(us => us.Genres, Genres);
await client.For<User> (CollectionName).Key(u.id).Set(u).UpdateEntriesAsync();
} catch (Exception e) {
Exception ex = e;
}
}
The first one, linkentryasync throws the exception
Number of parameter does not match expected count.
And the second one throws
Linked collection for type [Genre] not found
Any help would be great. I am stuck at work. Thanks in advance.
One immediate thing that you need to change is make properties Genre.Users and User.Genres public. Simple.OData.Client uses reflection to assign property values and is not capable of assigning values for private properties/fields. I tested your code with the schema you sent me and as long as the properties were public, the request went through.
Regarding the next example (using LinkEntryAsync), if you want to update links in a single call, you should use UpdateEntryAsync, because LinkEntryAsync does it for a single link. So either use:
var user = await client.For<User>("ApiUser").Key(1).FindEntryAsync();
user.Genres = genres;
await client.For<User>("ApiUser").Key(user).Set(user).UpdateEntryAsync();
or
foreach (var genre in genres)
{
await client.For<User>("ApiUser").Key(user).LinkEntryAsync(genre);
}
The first operation could have been written in a more efficient way:
await client.For<User>("ApiUser").Key(1).Set(new {Genres = genres}).UpdateEntryAsync();
That will generate HTTP PATCH instead of PUT with only Genres updated, but it looks like your OData service requires all mandatory properties to be sent on the entity being updated, so this won't work.
Last but not least: get the latest version (4.9.1) of Simple.OData.Client. It has a fix that is important for your scenario.
UPDATE. I tested your OData service, and it doesn't seem to have a proper support for addressing links. For example, if I test sample OData service, I can execute requests like http://services.odata.org/V4/OData/%28S%28ygi3rwu514y0a4ooybn3d1gc%29%29/OData.svc/Products%284002%29/Categories/$ref (note $ref segment that addresses Caterogories link so this URI can be used to post link updates). But if I execute request http://{your_service_uri}/ApiUsers%281%29/Genres/$ref then I get an error "No HTTP resource was found that matches the request URI 'http://partymag.azurewebsites.net/ApiUsers(1)/Genres/$ref'." As long as this link doesn't work on a server side you won't be able to use LinkEntryAsync or UnlinkEntryAsync but you can still use UpdateEntryAsync as I showed above.
UPDATE2. The version that uses UpdateEntryAsync executes fine but service doesn't update links, here is the result from Fiddler:
Generated URI: PATCH http://{your_service_uri}/ApiUsers(1)
PATCH payload:
{ "#odata.type":"#PMWeb.Models.Models.User",
"id":1,"Name":"Ege",
"LastName":"Aydin",
"Email":"{removed}",
"Password":"{removed}",
"Genres#odata.bind":[
"http://{your_service_uri}/Genre(31)","http://{your_service_uri}/Genre(32)"
]
}
Response:
{
"#odata.context":"http://{your_service_uri}/$metadata#ApiUsers/$entity",
"id":1,
"Name":"Ege",
"LastName":"Aydin",
"Email":"{removed}",
"Password":"{removed}"
}
If I now check the content of User's genres, they are the same. Since generated payload is correct and the service accepted it, it must be something on the server that is not executed properly.

Catch MongoAuthenticationException in Mongo .NET 2.0 Driver

I'm doing MongoDB project based on .NET 2.0 driver which involves authentication to MongoDB. There is a example code for what i'm doing:
public static bool createConneciton(string login, SecureString pass, string authDB) {
var settings = new MongoClientSettings {
Credentials = new[] {
MongoCredential.CreateCredential(authDB, login, pass)
},
Server = new MongoServerAddress("my.mongodb.server", 27017)
};
mongoClient = new MongoClient(settings);
return true;
}
if (Mongo.createConneciton(textBoxUsername.Text, pass, textBoxAuthDatabase.Text))
Task<BsonDocument> results = Mongo.getNodeStats();
public static async Task<BsonDocument> getNodeStats() {
try {
var db = Mongo.mongoClient.GetDatabase("admin");
var command = new BsonDocument {
{"serverStatus",1}
};
BsonDocument result = await db.RunCommandAsync<BsonDocument>(command).ConfigureAwait(false);
return result;
}
catch (Exception ex)
{
Logging.Log(ex);
return null;
}
}
Main problem i encountered so far is processing user's credentials. Because all operations are lazy and connection opens only on execution in getNodeStats() method. So if user types wrong credentials, he is going to wait for 30 seconds because instead of MongoDB.AuthenticationException or even MongoDB.ConnectionException method going to though only System.Timeout exception. If you look though text of exception that is quite obvious that both are rised but not catched.
"MongoDB.Driver.MongoConnectionException: An exception occurred while opening a connection to the server. ---> MongoDB.Driver.MongoAuthenticationException: Unable to authenticate using sasl protocol mechanism SCRAM-SHA-1
My first thought was to force open connection to check for credentials as soon as user typed them and hit connect button rather then waiting for any command to be executed, but apparently MongoClient class does not have .Open() method anymore. So if it does not seem to be possible i at least would like to catch AuthenticationException without need to wait for timeout, but out of ideas where should i try and catch it.
You cannot connect mongodb using MongoCredential.CreateCredential.You have to use MongoCredential.CreateMongoCRCredential method to connect the db. Because the former credential use SCRAM-SHA-1 mechanism to connect db, in .NET which will fail. And the reason I have not make clear.
Using MongoCredential.CreateMongoCRCredential, you have change "authSchema" setting in mongodb. You can refer to MongoDB-CR Authentication failed

How to call a WCF method asynchronously from MVC4 WebAPI

I am new to the WebApi, .Net world and am totally confused with all the information available as to what approach I should take. I have created a WebService using MVC4 WebApi that Twilio calls when a text message is received. I need to respond to this text message. I am consuming a WCF method which is currently being called synchronously. Since it is possible that my process can take longer than 3-5 seconds to process a reply to the text message the connection to Twilio gets disconnected due to timeout. So I am looking for ways to call this WCF method asynchronously.
My question is to call the WCF method (I am calling the WCF using a Object Factory and using)
do I need to update the contract to say Async? I am little confused on that.
BTW my Web Service is in IIS7 and am using .Net4.5 framework and MVC4 WebApi .
My code is somewhat like this: So I would like to call the SendSms part asynchronously. How do I do that? Can I simply use Task.Run Async and Await?
using Twilio.Mvc;
using Twilio.TwiML.Mvc;
using Twilio.TwiML;
public class SmsController : ApiController
{
[HttpPost]
public HttpResponseMessage Post([FromBody]SmsRequest smsReq)
{
var response = new Twilio.TwiML.TwilioResponse();
//validation checks..
try
{
-- call to WCF to get the List of sms to be sent
if ((txtMessageResponse != null) && (txtMessageResponse.SmsMessageInfo.Count > 0))
{
_smsStagingList = txtMessageResponse.SmsMessageInfo;
foreach (TextMessageStaging prepareTextMessageResponse in _smsStagingList)
{
smsDTO textMessageItems = new smsDTO();
textMessageItems.PhoneNumber = prepareTextMessageResponse.PhoneNumber;
textMessageItems.SmsMessage = prepareTextMessageResponse.SmsMessageBody;
isTxtMessageSent = SendSms(textMessageItems);
//If the messages were sent then no need to set the flag to be updated
if (isTxtMessageSent)
{
txtMessageStatusToBeUpdated = false;
}
}
return Request.CreateResponse(HttpStatusCode.OK, twilioResponse.Element);
}
else
{
//send error response
}
catch (Exception msgProcessingError)
{
//send error response again as processing error
}
finally
{
//set the outbound flag in the table
}
}
private bool SendSms(smsDTO textMessageItems)
{
bool isTxtMessageSent = false;
PushMessageRequest txtMessageRequest = new PushMessageRequest();
PushMessageResponse txtMessageResponse = null;
txtMessageRequest.SmsMessageInfo = new SendTextMessage(); //instantiate the dto
txtMessageRequest.SmsMessageInfo.ToPhone = textMessageItems.PhoneNumber;
txtMessageRequest.SmsMessageInfo.TextMessage = textMessageItems.SmsMessage;
try
{
using (ITextService textService = ObjectFactory.SendSmsMessage())
{
txtMessageResponse = textService.SendSmsMessage(txtMessageRequest);
}
isTxtMessageSent = txtMessageResponse.IsSuccessful;
}
catch (Exception ex)
{
isTxtMessageSent = false;
}
return isTxtMessageSent;
}
Twilio evangelist here.
OK, so you have a Web API method in which you call a WCF method that is potentially long running. There are two problems to solve here:
How do you call the WCF method in a way that does not block the Web API method from returning a response
How do you get Twilio to wait until the WCF method has finished
I wrote a blog post a while ago that shows you how to create an indefinite wait loop in an IVR by leveraging .NET's Task Parallel library and the loop attribute on Twilios <Play> verb.
The gist of the post is that you can use the TPL's StartNew method to start the long running WCF method on a new thread. This lets ASP.NET continue and lets you return some TwiML so Twilio does not end the call. Then you pair that with a continuation which lets you know when the WCF service request is done and you can signal back to Twilio using the REST API to redirect the in-progress call to a new set of TwiML instructions.
Hope that helps.

Categories

Resources