How to complete request before redirecting - c#

Using C#/Asp.Net
I have an application that goes out to a web service. On return there's a couple of things that happen:
void Cleanup(Response response)
{
// My web service takes up to 30 seconds
// then this method is called
// I send this email
var email = SaleEmail.Create(
response.ID
DateTime.Now,
"A sale was made!");
email.Send();
// Then redirect
Response.Redirect(response.RedirectUrl, false);
Context.ApplicationInstance.CompleteRequest();
}
The idea is, on completion of the web service an email is sent, then the page is redirected.
Previously, I used a normal redirect - the result was that 90% of the emails were never sent.
I've changed the redirect pattern, however it's still not perfect - I'm guessing 25% of emails are still not coming through.
Anyone advise any improvements to the pattern I have?
Email code:
public static void Send(MailMessage message)
{
Guard.Argument.NotNull(() => message);
var c = new SmtpClient();
try
{
c.Send(message);
}
catch (Exception ex)
{
}
finally
{
c.Dispose();
message.Dispose();
}
}

Maybe
Try to implement async task method with sendAsync and await
this await will help you to wait how much needed to send email before jump to redirect
//async Task
public async Task Cleanup(Response response)
{
using (var smtpClient = new SmtpClient())
{
await smtpClient.SendAsync();...//await
}
}

you should rewrite your initialization somehow, make it look like this:
smtpClient.SendAsync();
smtpClient.SendCompleted += new SendCompletedEventHandler(smtpClient_SendCompleted);
on smtpClient_SendCompleted function write your redirection code

Related

async/await seems to hang inside web method

In C# I have a web service with an operation result defined as below:
OperationResult<createAccountResponse> CreateAccount(string token, createAccountServiceModel model);
Inside that method I call another method with a signature indicating it is async, like so:
var sendEmailInvite = this.SendExhibitorInviteEmailAsync(new ExhibitorInviteEmailPartialRequest()
{
CompanyId = company.CompanyID,
EventId = event.EventID
});
And inside SendExhibitorInviteEmailAsync I await a method which is also marked as async. Here is that method (snipped for brevity)
public async Task<ExhibitorInviteEmailResponse> SendExhibitorInviteEmailAsync(ExhibitorInviteEmailResolvedRequest request)
{
ExhibitorInviteEmailResponse response = null;
try
{
response = new ExhibitorInviteEmailResponse();
var apiKey = "snip";
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage();
msg.SetFrom(new EmailAddress("noreply#domain.com", "Display name"));
msg.AddTo(new EmailAddress(request.EmailAddress, request.AccountOwnerFirstName));
msg.SetTemplateId("snipped");
\
msg.SetTemplateData(dynamicTemplateData);
await client.SendEmailAsync(msg);
}
catch (Exception ex)
{
response = new ExhibitorInviteEmailResponse
{
Success = false,
Error = true,
ErrorMessage = ex.Message
};
}
return response;
}
If the email is meant to be sent (flag field in the json) then I start working on the email.If no email is meant to be sent, the whole method takes about a second which was what it was before.
The issue I am having is when I run this method from Postman or from C# generated by Postman, it seems the async code for sending the email causes the duration of the request to be 30+ seconds - so it seems like something is not waiting for the email to send? When I run this via a browser it works in 1-2 seconds with no delay.
What is the recommended flow when using Postman and async? Do all internal method's parents have to await as well?

Bot Framework Sending Unnecessary Error Messages

I create a bot, called picturesaver, using Microsoft's Bot Framework, I added a GroupMe channel, and I have it hosted in Azure. The bot works perfectly, saving pictures to Google Drive.
However, the bot gives an error saying "Service Error:POST to picturesaver timed out after 15s" Is it possible to extend the timeout time? Or even stop the bot from posting anything at all. Could this be an Azure issue or is it a GroupMe issue?
If your bot performs an operation that takes longer than 15 seconds to process a message, you can process the message on another thread, and acknowledge the call right away. Something like:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
if ([determine if this will take > 15s])
{
// process the message asyncronously
Task.Factory.StartNew(async () => await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()));
}
else
{
//process the message normally
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
}
return Request.CreateResponse(HttpStatusCode.OK); //ack the call
}
This will avoid the 15 second timeout between connector and bot.
Edit: the above will not scale, and is just using a Task.Factory. Please refer to https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-long-operations-guidance for the recommended guidance on processing long operations from a bot.
The Bot Connector service has a 15s timeout so you need to make sure any async API calls are handled in that timeframe, or make sure your bot responds with some kind of message if it's waiting for some other operation to complete. Currently the 15s timeout cannot be modified.
The solution to process the message on another thread, and acknowledge the call right away is good only for a bot on an App Service.
But as for a Functions Bot doing so will finish the Azure Function if I immediately return from this method.
I tried it. The Azure Function stops running, and the real response to the chat never comes. So it's not a solution at all for the Function Bots.
I ended up with this code for a Functions Bot, which resolves this problem.
Using Azure Queues
public static class Functions
{
[FunctionName("messages")]
[return: Queue("somequeue")]
public static async Task<MessagePayload> Messages([HttpTrigger
(WebHookType = "genericJson")]HttpRequestMessage req) =>
// return from this Azure Function immediately to avoid timeout warning message
// in the chat.
// just put the request into "somequeue".
// We can't pass the whole request via the Queue, so pass only what we need for
// the message to be processed by Bot Framework
new MessagePayload
{
RequestUri = req.RequestUri,
Content = await req.Content.ReadAsStringAsync(),
AuthScheme = req.Headers.Authorization.Scheme,
AuthParameter = req.Headers.Authorization.Parameter
};
// Do the actual message processing in another Azure Function, which is
// triggered by a message enqueued in the Azure Queue "somequeue"
[FunctionName("processTheMessage")]
public static async Task ProcessTheMessage([QueueTrigger("somequeue")]
MessagePayload payload, TraceWriter logger)
{
// we don't want the queue to process this message 5 times if it fails,
// so we won't throw any exceptions here at all, but we'll handle them properly.
try
{
// recreate the request
var request = new HttpRequestMessage
{
Content = new StringContent(payload.Content),
RequestUri = payload.RequestUri
};
request.Headers.Authorization = new
AuthenticationHeaderValue(payload.AuthScheme, payload.AuthParameter);
// initialize dependency injection container, services, etc.
var initializer = new SomeInitializer(logger);
initializer.Initialize();
// handle the request in a usual way and reply back to the chat
await initializer.HandleRequestAsync(request);
}
catch (Exception ex)
{
try
{
// TODO: handle the exception
}
catch (Exception anotherException)
{
// swallow any exceptions in the exceptions handler?
}
}
}
}
[Serializable]
public class MessagePayload
{
public string Content { get; set; }
public string AuthParameter { get; set; }
public string AuthScheme { get; set; }
public Uri RequestUri { get; set; }
}
(Be sure to use different Azure Queues for local development with Bot Framework emulator and for a cloud-deployed Function App. Otherwise, the messages sent to your bot from real customers may be processed locally while you are debugging on your machine)
Using an HTTP request
Of course, the same can be done without using an Azure Queue with a direct call to another Azure Function's public URL - https://<my-bot>.azurewebsites.net/api/processTheMessage?code=<function-secret>. This call has to be done on another thread, without waiting for the result in the messages function.
[FunctionName("messages")]
public static async Task Run([HttpTrigger(WebHookType = "genericJson")]
HttpRequestMessage req)
{
// return from this Azure Function immediately to avoid timeout warning message
// in the chat.
using (var client = new HttpClient())
{
string secret = ConfigurationManager.AppSettings["processMessageHttp_secret"];
// change the RequestUri of the request to processMessageHttp Function's
// public URL, providing the secret code, stored in app settings
// with key 'processMessageHttp_secret'
req.RequestUri = new Uri(req.RequestUri.AbsoluteUri.Replace(
req.RequestUri.PathAndQuery, $"/api/processMessageHttp?code={secret}"));
// don't 'await' here. Simply send.
#pragma warning disable CS4014
client.SendAsync(req);
#pragma warning restore CS4014
// wait a little bit to ensure the request is sent. It will not
// send the request at all without this line, because it would
// terminate this Azure Function immediately
await Task.Delay(500);
}
}
[FunctionName("processMessageHttp")]
public static async Task ProcessMessageHttp([HttpTrigger(WebHookType = "genericJson")]
HttpRequestMessage req,
Microsoft.Extensions.Logging.ILogger log)
{
// first and foremost: initialize dependency
// injection container, logger, services, set default culture/language, etc.
var initializer = FunctionAppInitializer.Initialize(log);
// handle the request in a usual way and reply back to the chat
await initializer.HandleRequest(req);
}

Why does SmtpClient.SendAsync hang?

I am using SmtpClient.SendAsync (C#) to send emails in ASP.NET web applications and services. The web application/service runs on Windows Server 2012 R2 using IIS 8.
There are times where the call to SendAsync hangs up and does not appear to send the mail message asynchronously, so the calling thread blocks. This behavior seems to be sporadic. I cannot replicate the problem in a testing environment. This is especially problematic when sending the email as a result of a call to a web method because the timeout is 60 seconds (I'm using SendAsync for this very reason so the client doesn't experience any time delay).
Here is my code snippet.
SmtpClient client;
MailMessage msg;
public void SendMail()
{
try
{
client = new SmtpClient("smtpAddress#mydomain.com");
msg = new MailMessage();
msg.Subject = "Test";
msg.Body = "This is the body";
msg.From = "noreply#example.com";
msg.To.Add("me#example.com");
client.SendCompleted += new SendCompletedEventHandler(sendCompletedCallback);
client.SendAsync(msg, "Test");
}
catch (Exception ex)
{
// Log error
}
}
private void sendCompletedCallback(object send, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// Log error
}
else if (e.Cancelled)
{
// Log cancellation
}
else
{
// Log success
}
client.Dispose();
msg.Dispose();
}
Why does the call to SendAsync hang and block the calling thread at times?
Please check below link,
https://msdn.microsoft.com/en-us/library/x5x13z6h(v=vs.110).aspx
There might be two case, either it is not waiting for e-mail transmission to complete before attempting to send another e-mail message
or
recipients is invalid

How to send Newsletters (bulk emails) asynchronously in ASP.NET

I need to send about 5000 emails as newsletter to my website users asynchronously.. The problem is i don't know the best way to send them asynchronously ..Please help me updating my code to make it asynchronously
My Code:
public string SendEmail()
{
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
client.Send(message);
}
return "done";
}
Thank you , Lucy
Take a look at the async and await keywords.
https://msdn.microsoft.com/en-us/library/mt674882.aspx
The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources in the .NET Framework or the Windows Runtime to create an asynchronous method almost as easily as you create a synchronous method. Asynchronous methods that you define by using async and await are referred to as async methods.
MSDN explains the syntax side of things. The bigger concern is error handling and reliably. Dumping 5,000 emails into a list and hitting the "send" button on them is a little optimistic. Do these emails need to be reliably delivered? What happens if 3,000 of them send, and a network error suddenly causes temporary connectivity loss to the outgoing mail server? Are you going to resend all 5,000 when it starts working again? Just forget about the last 2,000? Are the recipients going to be mad because they got duplicates, or didn't get the message at all? How are you going to troubleshoot errors?
A pattern that I've found that has worked really well (whether you are sending synchronously or asynchronously), is to generate the messages and store each in a database table, and then use something like the following:
public void SendAllEmails()
{
var emails = SomeClass.GetAllUnsentEmails();
foreach(Email message in Emails)
{
var success = SendEmail(message);
if (!success)
{
// Do you want to do something if it fails?
}
}
}
public bool SendEmail(Email message)
{
try
{
// 1. Send the email message
// 2. Update the "SentOn" date in the database
// 3. return true
}
catch(Exception ex)
{
SomeClass.CreateEmailErrorEntry(message, ex); // store error in a table or log
return false;
}
}
edit: if it's being used inside of action you should mark your action async
public async Task<ActionResult> MyAction()
for a start, don't create SmtpClient for every message so as follows
and also this, should send the mails asynchronously but wait for all of them to be sent
public string SendEmail()
{
var tasks = new List<Task>();
var client = new SmtpClient("smtp.gmail.com", 587);
client.Credentials = new NetworkCredential("myemail#gmail.com", "*****");
client.EnableSsl = true;
foreach (var emailAddress in EmailList)
{
var message = new MailMessage("myemail#gmail.com", emailAddress);
message.Subject = "hi";
tasks.Add(client.SendMailAsync(message));
}
while(tasks.Count > 0)
{
var idx = Task.WaitAny(tasks.ToArray());
tasks.RemoveAt(idx);
}
return "done";
}

Windows Phone 8 HTTPWebRequests duplicating data

I am making an app for a chatroom that uses XHR Polling. I am able to correctly query the server and receive/send data from it, but I have no idea why the XHR Polling acts uncontrollably.
This is what I have to run the XHR Polling:
private void MakeInitialConnection() // Now that we have the session and all the required pieces of data, we can connect to the chatroom
{
this.XHRPolling.DoWork += (sender, args) => // Tell the XHR polling thread what to do
{
MakeConstantConnection();
};
this.XHRPolling.RunWorkerCompleted += (sender, args) => // Tell the thread what to do when the thread ends (i.e. the connection is cut)
{
this.OnDisconnect();
};
this.XHRPolling.RunWorkerAsync(); // Run the thread
}
private async void MakeConstantConnection()
{
while (!this.isChatQuit) // Continuously send XHR requests until the connection is determined to have ended
{
await this.SendXHR();
}
}
private async Task SendXHR()
{
HttpClient chatXHR = new HttpClient();
try
{
HttpResponseMessage response = await chatXHR.GetAsync(currentChatURL);
response.EnsureSuccessStatusCode();
string responseString = await response.Content.ReadAsStringAsync();
if (responseString == "1::")
{
this.OnConnect();
}
chatXHR.Dispose();
}
catch (HttpRequestException e)
{
this.OnErrorInConnection("SendXHR()", e.Message);
chatXHR.Dispose();
}
}
I get the data that I am expecting, however I keep getting the same data repeatedly until the app crashes. Even when I turn off networking entirely, I still keep getting duplicated data. So I have absolutely no idea what is going on. Doesn't HttpResponseMessage response = await chatXHR.GetAsync(currentChatURL); keep the request alive until it is fulfilled? Why is it being fulfilled when there is no network connection?
I have built a desktop app using almost the same code and that works exactly as intended. SendXHR must be done synchronously, otherwise the XHR polling wouldn't be XHR polling. Anyone have any ideas what is going on? I cannot seem to pinpoint the problem.

Categories

Resources