问题
In the code below I try to send a set of notifications and I want to know if a notification was sent successfully (to put it in a database later so never send it again).
Is it bad I'm catching Exception here? I really don't care about the reason a notification wasn't sent.
private static async Task<List<Tuple<NotificationToSend, bool>>> SendNotificationsAsync(IEnumerable<NotificationToSend> notificationsToSend)
{
var tuples = new List<Tuple<NotificationToSend, bool>>();
using (var smtpClient = new SmtpClient())
{
foreach (var notification in notificationsToSend)
{
bool sentSuccessfully;
try
{
var mailMessage = new MailMessage
{
Subject = notification.Subject,
Body = $"{notification.Text} <br /> This notification was sent automatically",
IsBodyHtml = true
};
mailMessage.To.Add(notification.ToEmail);
await smtpClient.SendMailAsync(mailMessage);
sentSuccessfully = true;
}
catch (Exception e)
{
sentSuccessfully = false;
// Here I also plan to log the exception
}
var tuple = new Tuple<NotificationToSend, bool>(notification, sentSuccessfully);
tuples.Add(tuple);
}
}
return tuples;
}
回答1:
You should never use catch(Exception e), you should only catch exceptions that you know can be thrown. And with the SmtpClient the MSDN documentation says that one of the following four exceptions can be thrown:
- ArgumentNullException
- InvalidOperationException
- ObjectDisposedException
- SmtpException
So you should do it like this:
var sentSuccessfully = false;
var mailMessage = new MailMessage
{
Subject = notification.Subject,
Body = $"{notification.Text} <br /> This notification was sent automatically",
IsBodyHtml = true
};
MailMessage.To.Add(notification.ToEmail);
try
{
await smtpClient.SendMailAsync(mailMessage);
sentSuccessfully = true;
}
catch (ArgumentNullException e)
{
sentSuccessfully = false;
// Handle Argument Exception
}
catch (InvalidOperationException e)
{
sentSuccessfully = false;
// Handle InvalidOperation Exception
}
catch (ObjectDisposedException e)
{
// This one shouldn't happen, so you could leave it out
sentSuccessfully = false;
// Do Handle ObjectDisposed Exception
}
catch (SmtpException e)
{
sentSuccessfully = false;
// Handle Smtp Exception
}
The reason for this is much better logging, if you know the exception that was thrown the log could display something like 'ArgumentNullException was thrown, *insert a possible explanation as to why*'
Or if you want to handle all exceptions the same way:
try
{
await smtpClient.SendMailAsync(mailMessage);
sentSuccessfully = true;
}
catch (Exception e) when (e is ArgumentNullException ||
e is InvalidOperationException ||
e is ObjectDisposedException ||
e is SmtpException)
{
sentSuccessfully = false;
// Handle exception
}
回答2:
Depends what behavior you want to achieve. Blanket catch like this will make program "limp" even if someone introduces a blunt error like null reference inside try...catch. Usually this is not desired and it's better to fail fast.
If you want program to carry on only after 'expected' SMTP infrastructural error then catch only specific types of exceptions (e.g. SmtpException or whatever can be thrown by SendMailAsync) and otherwise let the exception to bubble up.
回答3:
What I'd go for:
private static async Task<List<Tuple<NotificationToSend, bool>>> SendNotificationsAsync(IEnumerable<NotificationToSend> notificationsToSend)
{
var tuples = new List<Tuple<NotificationToSend, bool>>();
using (var smtpClient = new SmtpClient())
{
foreach (var notification in notificationsToSend)
{
bool sentSuccessfully = SendNotificationAsync(smtpClient, notification);
var tuple = new Tuple<NotificationToSend, bool>(notification, sentSuccessfully);
tuples.Add(tuple);
}
}
return tuples;
}
private static async Task<bool> SendNotificationAsync(SmtpClient smtpClient, NotificationToSend notification)
{
var mailMessage = new MailMessage
{
Subject = notification.Subject,
Body = $"{notification.Text} <br /> This notification was sent automatically",
IsBodyHtml = true
};
mailMessage.To.Add(notification.ToEmail);
try
{
await smtpClient.SendMailAsync(mailMessage);
return true;
}
catch (Exception e)
{
// Here I also plan to log the exception
return false;
}
}
来源:https://stackoverflow.com/questions/57588098/is-general-exception-handling-not-so-bad-in-this-case