Queuing method execution in asp.net - c#

I created messenger which uses AT commands and GSM modem to communicate but if many users send and receive messages at one time then AT commands will overlap and that will cause a lot of problems so I thought that my web app need sort of FIFO Queue for storing send/receive method execution.
Let's say I have HttpPost method which is executed by pressing SendMessage button on the web page this method saves the message to the database and executes SendSms and ReceiveSms method which doing stuff with AT commands as in the code below.
[HttpPost(Name = "add-message")]
public async Task<IActionResult> PostMessage([FromBody] MessengerViewModel messengerViewModel)
{
AtSmsSender smsSender = new AtSmsSender();
InnerAtSmsReceiver innerAtSmsReceiver = new InnerAtSmsReceiver(_receivedMessagesService);
await smsSender.SendSms(messengerViewModel.PhoneNr, messengerViewModel.MessageBody);
await innerAtSmsReceiver.ReceiveSms();
//Logic of saving message using service
}
I was searching for Queuing examples but mostly what I found was messages queuing which not fits this example as I think. How could I queue this Http method or methods inside it that they would be stacked and executed after some delay?
First time encountered this thing so sorry if I stated question too poorly.

Related

Azure Service Bus send message every other time

I've a c# dotnet webjob and a simple desktop app.
Sending a message apperaes to work only every other time.
serviceBusClient = new QueueClient(_config["ServiceBusConnectionString"], "queuename", ReceiveMode.ReceiveAndDelete);
await serviceBusClient.SendMigrationMessageAsync("1", label);
await serviceBusClient.SendMigrationMessageAsync("2", label);
await serviceBusClient.SendMigrationMessageAsync("3", label);
await serviceBusClient.SendMigrationMessageAsync("4", label);
SendMigrationMessageAsync is an extension:
public static async Task SendMigrationMessageAsync(this IQueueClient client, string messageText, string label)
{
Message message = new Message(Encoding.UTF8.GetBytes(messageText));
message.Label = label;
await client.SendAsync(message);
}
In the destkop app I registered to receive the message and also registered a message exception handler (which is not call at all).
In this scenario I can only receive message "2" and "4".
When I stopped execution after the first message had been sent, the message never showed up on the Azure service.
Thanks in advance
EDITED:
I found out that arter creating brand new Azure Service Bus Namespace, all is working fine.
I had basic pricing tier and even after upgrading to standard I was able to only send every other message.
Creating new service sorted this out.
Is there any limitation or throtling? I haven't sent many messages at all, something around 300 daily.
You most probably had two processes with the same subscription id, so they are "stealing" messages from each other. Let's say there are two console apps, the first one sending messages and the second one receiving.
With both having same subscription id it looks like this:
And with the unique subscription for each process everything is ok:

How to wait for another service to finish a task in the background without awaiting?

I'm creating an ASP.NET Core WebApi, which uses the Autodesk Model Derivative API to convert a Revit file into another file format. After I've uploaded the file, the Autodesk API starts working in the background and can take several minutes to finish its work.
I want to monitor the status of the Autodesk API, to know if the conversion has been finished yet and notify the user. I'm looking for the best way to monitor the status of the job without 'awaiting' and letting the request hang stuck for several minutes.
I've tried just running the task asynchronously without awaiting the result. This worked, up to the point where I wanted to update a value in my Database Context, because that had been disposed due to the request having ended.
I've also researched several options on implementing background services, but haven't found a clear way to do that.
public async Task<ActionResult<Response<JobResponse>>> UploadFile(string bucketKey, IFormFile file)
{
// ....
// File has been uploaded
Response<JobResponse> response
= await NetworkManager.PostAsync<JobResponse>(URI.Job.Url, JsonConvert.SerializeObject(jobData));
// The job has been created in the Autodesk API, so I create a record in my own database
var job = new Job(urn, file.FileName);
context.Jobs.Add(job);
await context.SaveChangesAsync();
// This method is what I want to do in the background
MonitorStatus(job);
return Respond(response);
}
private async Task MonitorStatus(Job job)
{
bool isDone = false;
while (!isDone)
{
isDone = await IsDone(job.Urn);
if (!isDone)
await Task.Delay(10000);
}
string guid = await new JobRepository(job).GetGuid();
// The line underneath throws an error because the context has been disposed
(await context.Jobs.FindAsync(job.Id)).Finish(guid);
await context.SaveChangesAsync();
// ...
// Notify the user
}
Translation of files in Model Derivative API boils down to two main endpoints:
POST job for triggering the translation, and
GET manifest for getting the manifest of the translation (incl. its status while the translation is still running)
If you're making the HTTP requests yourself, you can just poll the manifest until you see that the translation has been completed.
If you're using the Forge .NET SDK, you can trigger the translation using the Translate method, and poll the results using the GetManifest method.
I ended up using a Webhook of Autodesk Forge, which calls an endpoint that notifies the user that the conversion has been completed. This webhook contains a body with information about which job has been completed, so I can update the database accordingly.
This webhook removes the need for my MonitorStatus(job) method.
https://forge.autodesk.com/en/docs/webhooks/v1/tutorials/create-a-hook-model-derivative/

Send email async: An asynchronous module or handler completed while an asynchronous operation was still pending

I'm trying to send email from a Controller asynchronous and getting the following error:
I don't want to wait email be sent to complete the action.
An asynchronous module or handler completed while an asynchronous
operation was still pending.
This is my implementation: I also tried using void instead async Task
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
var user = await _userManager.FindByNameAsync(model.Email);
if (user != null)
{
if (!await _userManager.IsEmailConfirmedAsync(user.Id))
{
//dont want to await result
//just send email
_mailService.SendAccountConfirmationEmailAsync(user);
...
}
...
}
return View();
}
.
public async Task SendAccountConfirmationEmailAsync(ApplicationUser user)
{
dynamic email = GetAccountConfirmationEmailTemplate();
email.Username = user.Email;
email.Name = user.UserName;
email.To = user.Email;
email.AccountConfirmationUrl = await GetAccountConfirmationUrl(user);
await _mailService.SendAsync(email);
}
What am I missing?
Since the runtime can recycle your appdomain when it knows there are no more pending requests, starting tasks that you don't wait for is specifically not recommended, hence that message.
In particular, without this check you would have no guarantee this email would ever be sent since the entire appdomain could be yanked down around that task after your controller action returns.
Now in your case you might say that the loss of the email is acceptable, but the runtime doesn't know that it is only an email, it could very well be your database call that you've forgotten to wait for that would be prematurely aborted. Hence it just informs you that this is bad form.
Instead you got two options:
Wait for that task to complete, which you have specifically said you don't want to do
Inform the runtime that you're firing off a task that you specifically don't want to wait for, but please oh please, don't yank out the appdomain before task has completed
This last part is what HostingEnvironment.QueueBackgroundWorkItem is for.
In your case you would call it like this:
HostingEnvironment.QueueBackgroundWorkItem(ct =>
_mailService.SendAccountConfirmationEmailAsync(user));
Additionally, the method provides a CancellationToken, you should check if there is an overload of SendAccountConfirmationEmailAsync that accepts it. If not, and if this is your API, you should consider adding support for such tokens.
Please be aware that this mechanism is not meant for long-running threads or tasks, for that there are other more appropriate trees to bark up, but in this case it should be enough.
You need to await your async call in order to avoid the error message, but then you'll need to wait for the email to finish sending before your action method returns (and a failure sending the email will cause your action method to error out).
await _mailService.SendAccountConfirmationEmailAsync(user);
If you ever see a method name that ends in -Async, check if that method returns a Task or Task. If it does, you need to await it.
If you have an async call that you don't want to wait for it to complete in this context, you'll get that error. If you want to continue processing, you'll need some method of sending this email in a background task. That might be that you have a separate application that handles emails, and you communicate with it via a messaging system like RabbitMQ. Or there are ways to run code in the background directly in a website such as Hangfire.

Any downsides to replacing REST endpoints with SignalR?

I'm building a fairly simple single page app. It's basically a list of items, where each item has some details, an activity log, and a current status along with some buttons to trigger actions on the server to advance the status along a workflow.
It was originally written using MVC and REST/Web API but I got stuck on the problem of keeping concurrent users up to date. For example, if User A adds an item, we want the list on User B's screen to now update to include it.
To solve this I looked into SignalR which works great. But I had a problem.
When adding an item (using POST) the callback adds the item on the requesting client. This is fine.
I then triggered a SignalR broadcast on the server to tell all clients about the new item. This worked fine except the local client, who now has 2 items.
I was looking into filtering the duplicate id client-side, or sending the connection id with the POST, then broadcast to all clients except the requester but it seems a bit needlessly complicated.
Instead I'm just doing this.
public class UpdateHub : Hub
{
public void AddNewItem(NewItem item)
{
// and some server-side stuff, persist in the data store, etc
item.trackingID = new Guid();
item.addLogEntry("new item");
// ...
dataStore.addItem(item);
// send message type and data payload
Clients.All.broadcastMessage("add", item);
}
}
It seems a lot simpler to just get rid of all the REST stuff altogether, so am I missing anything important?
It'll run on an intranet for a handful of users using IE11+ and I guess we do lose some commonly-understood semantics around HTTP response codes for error handling, but I don't think that's a huge deal in this situation.
In order to solve duplicate you can try to use Clients.Others inside Hub class, or AllExcept(id) if you not in the Hub class.
Clients.Others.broadcastMessage("add", item);
In your case using SignalR shouldn`t have any downsides.

Calling method async from WCF service and return immediately

A third party is calling our WCF service. The caller wants confirmation, that the sent records have been received and stored, within a small timeframe.
The records that are stored need some lenghty processing. Can the processing be executed async, right after storing the records, so the confirmation can be send immediately?
Ofcourse there can be a separate process that does the processing, but the question is whether I can combine storage and processing without timing out.
Update:
It looks like this works:
var aTask = new Task(myService.TheMethod);
aTask.Start();
return aVariableAsync;
Or is this a very bad idea to do from within my WCF host, because.. ?
You can set "AsyncPattern" to true on the OperationContract attribute as described on MSDN.
You can then control the concurrency using the following attribute on the service method:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
Yes it can be done. I dont have a ton of experience with it, but this is a snippet of some code showing that. The service calls this method on the controller that saves an xml message to the hard drive and then kicks off a separate task to process it into MongoDB and returns a message back to the service that it was successfully saved.
public string SaveTransaction(XElement pTransactionXml, string pSavePath)
{
//save the transaction to the drive locally
pTransactionXml.Save(pSavePath);
...
var mongoTask = Task.Run(async () =>
{
await SendXMLFilesToMongo(pSavePath);
});
return response.WithResult("Successfully saved to disk.");
}
public virtual async Task<int> SendXMLFilesToMongo(string pSavePath)
{
//call the code to save to mongo and do additional processing
}

Categories