Reputation: 9153
i have created loop of sending emails if they fail
I have read that is not the best practise to have thread.sleep in code as this can lead to problems. However there is using for smtp client, which dispose of the transaction before the message is send. How can i make this better this is going to be used in web application?
using (var SmtpClient = new SmtpClient()) {
bool success = false;
int attemts = 0;
const int maxAttempts = 5;
do {
try {
SmtpClient.Send(mailMessage);
System.Threading.Thread.Sleep(400);
success = true;
} catch{
// ok wait for request
success = false;
attemts ++;
}
} while (success && (attemts == maxAttempts));
}
Upvotes: 3
Views: 6335
Reputation: 741
I want to just complement Dennis's answer.
Here the simplified implementation of the SmtpClient wrapper which allows you to use retry count parameter with SendAsync method:
public class EmailSender {
private int _currentRetryCount;
private int _maxRetryCount;
private MailMessage _mailMessage;
private bool _isAlreadyRun;
public event SendEmailCompletedEventHandler SendEmailCompleted;
public void SendEmailAsync(MailMessage message, int retryCount) {
if (_isAlreadyRun) {
throw new InvalidOperationException(
"EmailSender doesn't support multiple concurrent invocations."
);
}
_isAlreadyRun = true;
_maxRetryCount = retryCount;
_mailMessage = message;
SmtpClient client = new SmtpClient();
client.SendCompleted += SmtpClientSendCompleted;
SendMessage(client);
}
private void SendMessage(SmtpClient client) {
try {
client.SendAsync(_mailMessage, Guid.NewGuid());
} catch (Exception exception) {
EndProcessing(client);
}
}
private void EndProcessing (SmtpClient client) {
if (_mailMessage != null) {
_mailMessage.Dispose();
}
if (client != null) {
client.SendCompleted -= SmtpClientSendCompleted;
client.Dispose();
}
OnSendCompleted(
new SendEmailCompletedEventArgs(null, false, null, _currentRetryCount)
);
_isAlreadyRun = false;
_currentRetryCount = 0;
}
private void SmtpClientSendCompleted(object sender, AsyncCompletedEventArgs e) {
var smtpClient = (SmtpClient)sender;
if(e.Error == null || _currentRetryCount >= _maxRetryCount) {
EndProcessing(smtpClient);
} else {
_currentRetryCount++;
SendMessage(smtpClient);
}
}
protected virtual void OnSendCompleted(SendEmailCompletedEventArgs args) {
var handler = SendEmailCompleted;
if (handler != null) {
handler(this, args);
}
}
}
public delegate void SendEmailCompletedEventHandler(
object sender, SendEmailCompletedEventArgs e);
public class SendEmailCompletedEventArgs : AsyncCompletedEventArgs {
public SendEmailCompletedEventArgs(
Exception error,
bool canceled,
object userState,
int retryCount)
: base(error, canceled, userState) {
RetryCount = retryCount;
}
public int RetryCount { get; set; }
}}
Also below is the consumer code example:
var sender = new EmailSender();
sender.SendEmailCompleted += (o, eventArgs)
=> Console.WriteLine(eventArgs.RetryCount);
sender.SendEmailAsync(new MailMessage(), 5);
There are a lot of simplifications in the code fragment above, but you should understand the main idea.
Upvotes: 3
Reputation: 2937
It's not as easy as you think. It's easy to write it, but getting it to actually work is tricky :) . I suggest you take a look at this SMTP Sender. I spend some time trying to write my own email sender, but eventually found this working library. One of the things I had issues with was working with GMail, which is solved here. I am not connected to the author in any way, but I highly recommend this.
btw - yeah, it seems like .Net provides everything for you and there is no reason to use an external lib, but after you try a couple of times remember this post and try it.
Upvotes: 0
Reputation: 13673
I'm not exactly sure why you need this functionality, but I'm assuming you want to make sure an e-mail is sent.
In my experience it's best to let an e-mail server (or service) handle e-mail. E-mail servers are designed to retry delivery until it either succeeds or fails permanently. If you pick an e-mail server that is either local (like IIS's builtin e-mail service) or near by (your ISP for example) it should pretty much guarantee it's always available.
Configure SmtpClient
to send the e-mail there. Since the server is close by this should not fail and not take long at all, removing the need for retries in your application.
In any case, I would recommend you implement logging to keep track of any e-mails that could not be sent.
Upvotes: 1
Reputation: 51664
You can send the mail asynchronously using SmtpClient.SendAsync()
Sends the specified e-mail message to an SMTP server for delivery. This method does not block the calling thread and allows the caller to pass an object to the method that is invoked when the operation completes. -- MSDN
Upvotes: 0