Asp.Net : executionTimeout + parallel thread - asp.net

On a webpage, I launche a parallel process to do a long running task then send an email to keep track of its "output".
But the thread sometimes seems to end before sending the email (the email is the only track Ihave) and I think it might be related to a timeout.
Here the code:
var thread = new Thread(() =>
{
var cli = Factory.GetClient(callBack);
FedDBService.ReturnMessage msg;
try
{
msg = cli.SendFedCards(xmls.ToArray());
}
catch (Exception exe)
{
msg = new FedDBService.ReturnMessage();
msg.Error = exe.Message;
}
finally
{
cli.Close();
completion.Finished = true;
}
message = String.Format(#"{0}Ended: {1: HH:mm} {2} {3}", message, DateTime.Now,
msg.Error.Trim(' ', '\n', '\r', '\t') == "" ?
"Withouth errors" : "With errors:"
, msg.Error);
try
{
golf.classes.Mailing.Mail.sendMail(Club.ClubEmail, email, null,
"***#***.com", message, "**Subject**", false);
// this method works right
}
finally
{
Thread.Sleep(5 * 60 * 1000);
RemoveCompletion(guid);
}
});
thread.Start();
Do you think it's related to some timeout? And can I change it withouth allowing usual requests to run forever?

You shouldn't start background work in a web app - if the App Pool is recycled, your background work will be aborted with extreme predjudice.
Phil Haack wrote a blog about this topic here:
The Dangers of Implementing Recurring Background Tasks In ASP.NET
The recommended solution is to pass this work to a seperate Windows Service.

Related

.Net Core SignalR: how to persist connections

I have an infinitely running process that pushes events from a server to subscribed SignalR clients. There may be long periods where no events take place on the server.
Currently, the process all works fine -- for a short period of time-- but eventually, the client stops responding to events pushed by the server. I can see the events taking place on the server-side, but the client becomes unaware of the event. I am assuming this symptom means some timeout period has been reached and the client has unsubscribed from the Hub.
I added some code to reconnect if the connection was dropped, and that has helped, but the client still eventually stops seeing new events. I know there are many different timeout values that can be adjusted, but it's all pretty confusing to me and not sure if I should even be tinkering with them.
try
{
myHubConnection = new HubConnectionBuilder()
.WithUrl(hubURL, HttpTransportType.WebSockets)
.AddMessagePackProtocol()
.AddJsonProtocol(options =>
{
options.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
})
.Build();
// Client method that can be called by server
myHubConnection.On<string>("ReceiveInfo", json =>
{
// Action performed when method called by server
pub.ShowInfo(json);
});
try
{
// connect to Hub
await myHubConnection.StartAsync();
msg = "Connected to Hub";
}
catch (Exception ex)
{
appLog.WriteError(ex.Message);
msg = "Error: " + ex.Message;
}
// Reconnect lost Hub connection
myHubConnection.Closed += async (error) =>
{
try
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await myHubConnection.StartAsync();
msg = "Reconnected to Hub";
appLog.WriteWarning(msg);
}
catch (Exception ex)
{
appLog.WriteError(ex.Message);
msg = "Error: " + ex.Message;
}
};
This all works as expected for a while, then stops without errors. Is there something I can do to (1) ensure the client NEVER unsubscribes, and (2) if the connection is lost (network outage for example) ensures the client resubscribes to the events. This client must NEVER timeout or give up trying to reconnect if required.

Using speech synthesizer in ASP.NET web application gets stuck

In an MVC web application I use the SpeechSynthesizer class to speak some text to a .wav file during a function called by a controller action handler that returns a view. The code executes, writes the file, and the action handle returns, but the development server usually, but not always, never comes back with the return page. This is the text-to-speech code:
string threadMessage = null;
bool returnValue = true;
var t = new System.Threading.Thread(() =>
{
try
{
SpeechEngine.SetOutputToWaveFile(wavFilePath);
SpeechEngine.Speak(text);
SpeechEngine.SetOutputToNull();
}
catch (Exception exception)
{
threadMessage = "Error doing text to speech to file: " + exception.Message;
returnValue = false;
}
});
t.Start();
t.Join();
if (!returnValue)
{
message = threadMessage;
return returnValue;
}
I saw a couple of posts for a similar problem in a service that advised doing the operation in a thread, hence the above thread.
Actually, using the SpeechSynthesizer for other things can hang as well. I had a page that just enumerated the voices, but it would get stuck as well. Since there is no user code in any of the threads if I pause the debugger, I have no clue how to debug it.
I've tried Dispose'ing the SpeechSynthesizer object afterwards, calling SetOutputToDefaultVoice, to no avail. I've tried it on both Windows 8.1 and Windows 8, running with the development server under the debugger, or running IIS Express separately.
Any ideas? Is there other information I could give that would be helpful?
Thanks.
-John
Try
Public void Speak(string wavFilePath, string text)
{
using (var synthesizer = new SpeechSynthesizer())
{
synthesizer.SetOutputToWaveFile(wavFilePath);
synthesizer.Speak(text);
return outputFile;
}
}
Task.Run(() => Speak("path", "text")).Result;
It worked for me in IIS Express

Is there a notification when ASP.NET Web API completes sending to the client

I'm using Web API to stream large files to clients, but I'd like to log if the download was successful or not. That is, if the server sent the entire content of the file.
Is there some way to get a a callback or event when the HttpResponseMessage completes sending data?
Perhaps something like this:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// This doesn't exist, but it illustrates what I'm trying to do.
response.OnComplete(context =>
{
if (context.Success)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
EDIT: I've now tested this using a real connection (via fiddler).
I inherited StreamContent and added my own OnComplete action which checks for an exception:
public class StreamContentWithCompletion : StreamContent
{
public StreamContentWithCompletion(Stream stream) : base (stream) { }
public StreamContentWithCompletion(Stream stream, Action<Exception> onComplete) : base(stream)
{
this.OnComplete = onComplete;
}
public Action<Exception> OnComplete { get; set; }
protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
var t = base.SerializeToStreamAsync(stream, context);
t.ContinueWith(x =>
{
if (this.OnComplete != null)
{
// The task will be in a faulted state if something went wrong.
// I observed the following exception when I aborted the fiddler session:
// 'System.Web.HttpException (0x800704CD): The remote host closed the connection.'
if (x.IsFaulted)
this.OnComplete(x.Exception.GetBaseException());
else
this.OnComplete(null);
}
}, TaskContinuationOptions.ExecuteSynchronously);
return t;
}
}
Then I use it like so:
var stream = GetMyStream();
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContentWithCompletion(stream, ex =>
{
if (ex == null)
Log.Info("File downloaded successfully.");
else
Log.Warn("File download was terminated by client.");
});
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return response;
I am not sure if there is direct signaling that all is ok, but you can use a trick to find out that the connection is exist just before you end it up, and right after you fully send the file.
For example the Response.IsClientConnected is return true if the client is still connected, so you can check something like:
// send the file, make a flush
Response.Flush();
// and now the file is fully sended check if the client is still connected
if(Response.IsClientConnected)
{
// log that all looks ok until the last byte.
}
else
{
// the client is not connected, so maybe have lost some data
}
// and now close the connection.
Response.End();
if the server sent the entire content of the file
Actually there is nothing to do :)
This might sound very simplistic but you will know if an exception is raised - if you care about server delivering and not client cancelling halfway. IsClientConnected is based on ASP.NET HttpResponse not the WebApi.

quartz scheduler sending multiple email notifications

All i am using a quartz schedular for scheduling a job in an asp.net mvc application.This schedular schedules a job after fixed interval of time.
http://quartznet.sourceforge.net/
The service i have created basically runs every minute.It reads the message from the
message que(database in my case) every 1min , sends an email and updates the message sent status
to true.
I am having some problems though.TO be specific the problem is the service sends the same email twice because of the reasons mentioned below.
In some cases the service gets called as soon as an email is send before the db update happens.As The database update does not happen after sending email and service is invoked again,the processed message is again read from the database as unread message and gets resent.
The same message is read again from database.Thus the service ends of sending same message twice.
How do i handle this case in my code.
public void Execute(JobExecutionContext context)
{
List<QueuedEmail> lstQueuedEmail =
_svcQueuedEmail.Filter((x => x.IsSent == false)).Take(NO_OF_MAILS_TO_SEND).ToList();
if (lstQueuedEmail.Count > 0)
{
foreach (var queuedEmail in lstQueuedEmail)
{
try
{
bool emailSendStatus = false;
emailSendStatus = EmailHelper.SendEmail(queuedEmail.From, queuedEmail.To, queuedEmail.Subject,
queuedEmail.Body, queuedEmail.FromName);
QueuedEmail objQueuedEmail =
_svcQueuedEmail.Filter(x => x.Id == queuedEmail.Id).FirstOrDefault();
if (emailSendStatus)
{
objQueuedEmail.IsSent = true;
objQueuedEmail.SentOnUtc = DateTime.UtcNow;
}
else
{
objQueuedEmail.IsSent = false;
if (objQueuedEmail.SentTries == null)
{
objQueuedEmail.SentTries = 1;
}
else
{
objQueuedEmail.SentTries += 1;
}
}
_svcQueuedEmail.Update(objQueuedEmail);
}
catch (Exception)
{
//log error
}
}
}
}
Assuming you have two states for an email: "Pending" and "Sent".
You should add a third an intermediary state called "Sending" and as soon as you read the email from the Queue you should change it's status to something like "Executing" so other threads/services won't get it again.

threading in asp.net

I have a site I manage for a client and they wanted to be able to send out emails to all of their membership. I contacted the host and they suggested writing it in a way that it sends out in batches of 50 or less every minute so the mail server doesn't get overloaded.
That sounds great but the only way I could think of to do this without causing the administrator to have to sit on a page while it sends emails and reloads between each batch was to have a page call an ashx handler which fired up a thread to do the work and the thread is set to sleep after each batch for 60 seconds.
When I run the code from my machine it works fine and completes the entire list of emails. When I run it from the web host, which I don't have access to aside from ftp, it nearly completes but doesn't. Then if I try to hit the ashx page again to finish any that weren't sent, it doesn't do anything. It's like the thread causes something to lock up maybe and keeps additional threads from running.
Here's the code I'm using and I've never used threading before... so, does anyone know why it might be doing this and how to make it work correctly? Do I need to specifically kill the thread after I'm done? If so, how? Thanks.
public void ProcessRequest(HttpContext context)
{
if (context.Request.QueryString["id"].IsValid<int>())
{
campaignId = context.Request.QueryString["id"].To<int>();
var t = new Thread(new ThreadStart(SendEmails))
{
Priority = ThreadPriority.Lowest
};
t.Start();
}
}
private void SendEmails()
{
int currentCount = 0;
BroadcastEmailCampaign campaign = EmailController.GetCampaign(campaignId, false);
List<Member> memberlist = EmailController.GetEmailList(campaign.CampaignId);
var message = new MailMessage
{
Body = campaign.Body,
From = new MailAddress(campaign.SentBy),
IsBodyHtml = true,
Subject = campaign.Subject,
BodyEncoding = Encoding.UTF8
};
//add attachment
if (!string.IsNullOrEmpty(campaign.Attachment) && File.Exists(campaign.Attachment))
{
var attachment = new Attachment(campaign.Attachment);
EmailAttachmentType.SetContentProperites(campaign.Attachment, ref attachment);
message.Attachments.Add(attachment);
}
if (memberlist.Count <= 0)
{
return;
}
bool sendingComplete = false;
EmailController.SetCampaignSendingStatus(true, campaign.CampaignId);
while (sendingComplete == false)
{
message.Bcc.Clear();
message.To.Clear();
message.To.Add(new MailAddress(dummyEmailAddress));
List<Member> emailsToSend = memberlist.Skip(currentCount).Take(takeCount).ToList();
if (emailsToSend.Count <= 0)
{
sendingComplete = true;
EmailController.LogEmailCampaignResult(campaign);
EmailController.SetCampaignSendingStatus(false, campaign.CampaignId);
}
if (!sendingComplete)
{
foreach (Member email in emailsToSend)
{
message.Bcc.Add(new MailAddress(email.Email));
campaign.SentTo.Add(new BroadcastEmailCampaignSentTo
{
MemberId = email.MemberId,
Email = email.Email,
DateSent = DateTime.Now
});
}
EmailController.SendEmail(message);
EmailController.LogEmailsSent(emailsToSend, campaignId);
currentCount += takeCount;
Thread.Sleep(pauseTime);
}
}
}
Since I read a lot of threading in ASP.NET and still have no real clue of the dos and donts, I usually solve tasks like you describe by a console application that runs as a Scheduled Task in Windows Task Scheduler every e.g. 5 minutes:
In the ASP.NET page, I write all required information into a database table.
The scheduler periodically polls the database table for new jobs (e.g. sending of an e-mail) and processes, then empties the database table that serves as a queue.
This enables my application to stay responsive and in addition I don't have to worry that an IISRESET or something like this would kill my background threads.
t.IsBackground=true;
If that doesn't do it, I suggest using the ThreadPool with QueueUserWorkItem.

Resources