How to catch specific exceptions when sending mail? - c#

I have the following piece of code
try
{
if (!bDebug)
smtp.Send(m);
}
catch (Exception e)
{
wl("Meldingen kunne ikke sendes til en eller flere mottakere.", ConsoleColor.Red);
wl(e.Message, ConsoleColor.DarkRed);
using (var errorfile = System.IO.File.CreateText("error-" + DateTime.Now.Ticks + ".txt"))
{
errorfile.WriteLine(e.StackTrace);
if (e.GetType() == typeof(SmtpFailedRecipientException))
{
var se = (SmtpFailedRecipientException) e;
errorfile.WriteLine(se.FailedRecipient);
}
errorfile.WriteLine(e.ToString());
}
}
Where wl is a shortcut for writing to the console with color, and the text in the first line says "The message could not be sent to one or more recipients.
Previously I only caught the SmtpFailedRecipientException, but when it started failing in some other steps I shoved the generic Exception in there. So the part I'm wondering about is where I'm casting the Exception object into a more specific object to get the FailedRecipient property. Could/should this be done in another more proper way? It seems a bit clunky...

You can have multiple catch branches:
catch (SmtpFailedRecipientException se)
{
using (var errorfile = System.IO.File.CreateText("error-" + DateTime.Now.Ticks + ".txt"))
{
errorfile.WriteLine(se.StackTrace);
// variable se is already the right type, so no need to cast it
errorfile.WriteLine(se.FailedRecipient);
errorfile.WriteLine(se.ToString());
}
}
catch (Exception e)
{
wl("Meldingen kunne ikke sendes til en eller flere mottakere.", ConsoleColor.Red);
wl(e.Message, ConsoleColor.DarkRed);
// for other error types just write the info without the FailedRecipient
using (var errorfile = System.IO.File.CreateText("error-" + DateTime.Now.Ticks + ".txt"))
{
errorfile.WriteLine(e.StackTrace);
errorfile.WriteLine(e.ToString());
}
}

You can try somthing like this(source):
We're going to learn how to catch/handle different types of
exceptions/errors that might occur while sending an email using
ASP.Net. We'll implement error/exception handling using different
exception classes available in System.Net.Mail.
First to learn how to send an email using ASP.Net visit this
link. Notice that in the above article (lead by link) the
'SendEmails' catches only a generic exception and in case ASP.Net
encounters an error while sending email it would be like 'Sending
email failed etc'. We'll extend the error handling functionality for
the above article. So lets get started by openning the solution we
created previously. We already have put a try-catch block that catches
a generic exception that tells very little about what might have gone
wrong. Let's catch different types of exception right away:
Catch the SmtpException: The SmtpException class has a property
'StatusCode' which is actually an enumeration that gets the
error/exception code value returned by the SMTP server when an email
message is transmitted. It also provides more details of
error/exception that can occur during the email sending process. e.g.
catch (SmtpException smtpException)
{ // You can put a switch block to check for different exceptions or errors
// To checks if the destination mailbox is busy
if (smtpException.StatusCode == SmtpStatusCode.MailboxBusy)
throw smtpException;
// To check if the client is authenticated or is allowed to send email using the specified SMTP host
if (smtpException.StatusCode == SmtpStatusCode.ClientNotPermitted)
throw smtpException;
// The following code checks if the email message is too large to be stored in destination mailbox
if (smtpException.StatusCode == SmtpStatusCode.ExceededStorageAllocation)
throw smtpException;
// To check if the email was successfully sent to the SMTP service
if (smtpException.StatusCode == SmtpStatusCode.Ok)
throw smtpException;
// When the SMTP host is not found check for the following value
if (smtpException.StatusCode == SmtpStatusCode.GeneralFailure)
throw smtpException;
}
Catch the SmtpFailedRecipientException: The
SmtpFailedRecipientException class deals with the exception related to
the recipient of the email e.g. SMTP is not able to send the email to
a recipient. The SmtpFailedRecipientException occurs when SmtpClient
is not able to complete a SmtpClient.Send() or SmtpClient.SendAsync()
operation to a particular recipient. To catch this exception use the
following code:
catch (System.Net.Mail.SmtpFailedRecipientException smtpFailedRecipientException)
{
// Get the email that is causing email sending failed exception
String emailCausingException = smtpFailedRecipientException.FailedRecipient;
// Get the status code why and what is actually causing an email sending error
System.Net.Mail.SmtpStatusCode statusCode = smtpFailedRecipientException.StatusCode;
// Take some action either re-send the email again or do some error handling code here
}
Catch the SmtpFailedRecipientsException: The
SmtpFailedRecipientsException is actually a collection of
SmtpFailedRecipientException objects serving the same purpose. It is
used to handle exceptions when SmtpClient is not able to send emails
to one or more recipients.
catch (System.Net.Mail.SmtpFailedRecipientsException smtpFailedRecipientsException)
{
ArrayList emailCausingException = new ArrayList();
foreach (SmtpFailedRecipientException smtpFailedRecipientException
in smtpFailedRecipientsException.InnerExceptions)
{
// Get the email that is causing email sending failed exception
// Add it to a list of emails with exceptions
emailCausingException.Add(smtpFailedRecipientException.FailedRecipient);
// Get the status code why and what is actually causing an email sending error
System.Net.Mail.SmtpStatusCode statusCode = smtpFailedRecipientException.StatusCode;
// Take some action either re-send the email again or do some error handling
// You can also log or print this status code for an individual recipient here
if (statusCode == SmtpStatusCode.MailboxBusy)
{
//Re-Send email after some time
System.Threading.Thread.Sleep(1000);
//smtpClient.Send();
//Email sending code here
}
}
}

Related

Exchange Web Service – “You must load or assign this property before you can read its value” error message when trying to read message property

I have a mailbox that receives an automated email every 5 mins from a remote station. contained within that email is a string that needs to be compared with the same string from the previous email.
I am trying to automate this process for obvious reasons.
So far i am able to read the ConversationTopic of the emails, however, i can't seem to figure out how to read the content of the emails.
when it call this:
email.Load();
MessageBox.Show(email.TextBody.Text.ToString());
i get the following error:
You must load or assign this property before you can read its value
I have had a google and i can't find anything that relates to my instance, so any help would be great.
This is my full code so far:
private void Form1_Load(object sender, EventArgs e)
{
try
{
//MessageBox.Show("Registering Exchange connection");
_service = new ExchangeService
{
Credentials = new WebCredentials("myaddy#domain.com", "*****")
};
}
catch
{
MessageBox.Show("new ExchangeService failed.");
return;
}
// This is the office365 webservice URL
_service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
// Prepare seperate class for writing email to the database
try
{
//MessageBox.Show("Reading mail");
// Read 100 mails
foreach (EmailMessage email in _service.FindItems(WellKnownFolderName.Inbox, new ItemView(10)))
{
if (email.ConversationTopic.ToString().Contains("from RockBLOCK 300234066454740"))
{
email.Load();
MessageBox.Show(email.TextBody.Text.ToString());
}
}
MessageBox.Show("Exiting");
}
catch (Exception ex)
{
MessageBox.Show("An error has occured. \n:" + ex.Message);
}
}
The exception is thrown because you're trying to read the property Item.TextBody. This property is not a first-class email property.
The docs say:
Not all important email properties and elements are first-class
properties and elements. To get the other properties or elements, you
need to add them to your PropertySet if you're using the EWS Managed
API, or use a property path to add them to your EWS operation call.
For example, to retrieve the text body ... , create your PropertySet ...
In your case:
email.Load(new PropertySet(EmailMessageSchema.ConversationTopic, ItemSchema.TextBody));
Using this request EWS will load and return an EmailMessage with the two properties from the PropertySet.
NOTE:
By specifying a PropertySet with the properties you need to work with, EWS may process your request faster since it has not to search for all first-class-email properties. Moreover you will not run in such an error, where you're trying to read a property, which isn't a member of the first-class-email properties.

Any way to verify a service bus trigger to SendGridMessage succeeded before executing code?

I've got an azure function right now that runs on a service bus trigger (queue trigger) and outputs a SendGridMessage. The trick is I need to do some cleanup in my blob storage after the function has successfully sent a sendgrid message but it seems like I have no way of identifying whether or not the function was successful until after it goes out of scope.
I'm currently attempting to push the message that needs to be cleaned up to a cleanup queue and take care of it after the try catch but I think I'm still running into the same problem. The function could succeed and then fail on the SendGrid output and the message would be cleaned up but thrown back into the queue to be reprocessed on this function and fail. Bleh.
Queue Trigger and Sendgrid Output
[FunctionName("ProcessEmail")]
public static void Run([ServiceBusTrigger("email-queue-jobs", AccessRights.Manage,
Connection = "MicroServicesServiceBus")]OutgoingEmail outgoingEmail, TraceWriter log,
[ServiceBus("email-queue-cleanup", Connection = "MicroServicesServiceBus",
EntityType = Microsoft.Azure.WebJobs.ServiceBus.EntityType.Queue)] IAsyncCollector<OutgoingEmail> cleanupEmailQueue,
[SendGrid] out SendGridMessage message)
{
try
{
log.Info($"Attempting to send the email {outgoingEmail.Id}");
message = SendgridHelper.ConvertToSendgridMessage(outgoingEmail);
log.Info("Successfully sent email:");
log.Info(JsonConvert.SerializeObject(outgoingEmail));
}
catch (Exception ex)
{
message = null;
throw ex;
}
// Add email to the cleanup queue
log.Info("Sending email to the cleanup queue.");
cleanupEmailQueue.AddAsync(outgoingEmail).Wait();
}
You should be able to achieve this by using ICollector or IAsyncCollector
[SendGrid] ICollector<SendGridMessage> messageCollector)
and then
var message = SendgridHelper.ConvertToSendgridMessage(outgoingEmail);
messageCollector.Add(message);
should call SendGrid synchronously and throw exception in case of failure.
If you want to use IAsyncCollector (as you already do for another binding), be sure to call FlushAsync method too:
[SendGrid] IAsyncCollector<SendGridMessage> messageCollector)
and then
var message = SendgridHelper.ConvertToSendgridMessage(outgoingEmail);
await messageCollector.AddAsync(message);
await messageCollector.FlushAsync();

c# sharp proejct .net 4.5 dependency, should interact with a azure message queue

I am currently working on a project that depends on .net 4.5, which I am why forced to use Microsoft.Servicebus - and thus message factory
Creating a messagefactory object, doesn't seem to be problem, but creating the message sender seem to be the issue here.
I am not sure what the entity path for my azure message queue is, and don't know how I should look it up?
I initialized my message factory as such;
public Microsoft.ServiceBus.Messaging.MessagingFactory client = Microsoft.ServiceBus.Messaging.MessagingFactory.CreateFromConnectionString(ServiceBusConnectString);
Where servicebusconnectionstring, is the primary conenctionstring extracted from the azure queue, or by following this guide: https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-get-started-with-queues#1-create-a-namespace-using-the-azure-portal
The message sender is created as such:
Microsoft.ServiceBus.Messaging.MessageSender sender = client.CreateMessageSender(destinationQueue,queueName);
Again destionationQueue, and queueName are strings, which are defined from azure?
The message is to be send is created as such:
Microsoft.ServiceBus.Messaging.BrokeredMessage message = new Microsoft.ServiceBus.Messaging.BrokeredMessage(e.ToXml());
e is just an object, which has publich function which can convert it to an xml string.
Here is where I send the message:
try
{
IntegrationLogger.Write(LogLevel.Verbose, "Sending message to azure");
sender.Send(message);
}
catch(System.Exception ex)
{
if(ex is System.TimeoutException)
{
IntegrationLogger.Write(LogLevel.Error, "Timeout exeption");
}
if (ex is System.ArgumentException)
{
IntegrationLogger.Write(LogLevel.Error, "Message is empty");
}
}
IntegrationLogger is a log function I use - and the last message being logged is "Sending message to azure", meaning somethng goes wrong when I send it.
even catch does not catch the exception?..
What could be the problem here?
Am I using the sender incorrectly?
I am not sure what the enity path for my azure message queue is, and don't know how i should look it up?
If we want to create queue MessageSender, we could use following code.
var sender = client.CreateMessageSender(queueName);
We also could use queueclient to send and receive the broker messsage.
var client = QueueClient.CreateFromConnectionString("servicebus connection string", queueName);
// how to send message
var sendMessage = new BrokeredMessage(data);
client.Send(sendMessage);
//how to receive message
client.OnMessage(message =>
{
var dataInfo = message.GetBody<T>();
});
IntegrationLogger is a log function I use - and the last message being logged is "Sending message to azure", meaning somethng goes wrong when I send it. even catch does not catch the exepetion?..
In your case I recommand that you could add the log info to check whether there is any exception during send message.
try
{
IntegrationLogger.Write(LogLevel.Verbose, "Sending message to azure");
sender.Send(message);
//add log
IntegrationLogger.Write(LogLevel.Verbose, "Send message successfully");
}

TLSharp Data center (dc) Exception

I am using TLSharp library for implementing a custom Telegram client. when I run the code below:
public async Task<string> SendCodeRequest(string phoneNumber)
{
var completed = false;
TL.AuthSendCodeRequest request = null;
while (!completed)
{
request = new TL.AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
await _sender.Send(request);
await _sender.Receive(request);
completed = true;
}
// TODO handle other types (such as SMS)
if (request.Result is TL.AuthSentCodeType)
{
var result = (TL.AuthSentCodeType)request.Result;
return result.PhoneCodeHash;
}
else
{
var result = (TL.AuthSentAppCodeType)request.Result;
return result.PhoneCodeHash;
}
}
I gives me the following exception :
Your phone number registered to {dcIdx} dc. Please update settings.
See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for
details.
The mentioned github page says that TLSharp Handles these exceptions by itself. So I guess something is wrong with the library core because the code should resolve data center IPs by itself not generating an exception.
Any help would be appreciated.
TlSharp Currently doesn't handle this exception you have to catch the exception and get the data center number then Try to reconnect to the Data Center using the ReconnectToDc() Function.
In the MtProtoSender.cs file you can find the following line of code that generates the exception:
throw new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details.");
Replace it with the following code so that the Exception that is generated has the required Data Center number for connecting to it.
InvalidOperationException exception = new InvalidOperationException($"Your phone number registered to {dcIdx} dc. Please update settings. See https://github.com/sochix/TLSharp#i-get-an-error-migrate_x for details.");
exception.Data.Add("dcId", dcIdx);
throw exception;
Change your code like this:
while (!completed)
{
request = new TL.AuthSendCodeRequest(phoneNumber, 5, _apiId, _apiHash, "en");
try
{
await _sender.Send(request);
await _sender.Receive(request);
completed = true;
}
catch (InvalidOperationException ex)
{
if (ex.Message.StartsWith("Your phone number registered to") && ex.Data["dcId"] != null)
{
await ReconnectToDc((int)ex.Data["dcId"]);
}
else
{
throw;
}
}
}
In the Code above the Data Center number that was attached to the Exception is used for reconnecting to the Data center.
Probably your phone number is not in the format accepted by Telegram.
Phone number must start with plus sign, use country code and phone number without gap, for example: +989333333333

Get sent mail error

Is there any way to get sent error from the smtp to check if the mail is sent successfully?
var smtpClient = new SmtpClient("SmtpServer");
smtpClient.SendCompleted += new SendCompletedEventHandler(SendCompletedCallback);
smtpClient.SendAsync(mail, userId);
The errors I am looking for are: mail can't be deliver because the mail address not exists, mail box full etc...
Regards,
Meir.
I am not sure what you are trying to achieve but this will helps you.
I assume you're already aware of the DeriveryNotificationOptions property on System.Net.Mail.MailMessage. The only tricky part to using that property is that its enum type represents a bitfield, so you should set it to the sum of the options you want to apply.
For example, if you want delivery notification on delay, failure, or success, you should set the property to
DeliveryNotificationOptions.Delay + DeliveryNotificationOptions.OnFailure + DeliveryNotificationOptions.OnSuccess
Or
this is one method to capture the failure report or any error when the mail has not been sent (failure report)
// Change your Try-Catch to call the new method named 'CheckExceptionAndResend'
// Error handling for sending message
try
{
smtpClient.Send(message);
// Exception contains information on each failed receipient
}
catch (System.Net.Mail.SmtpFailedRecipientsException recExc)
{
// Call method that will analyze exception and attempt to re-send the email
CheckExceptionAndResend(recExc, smtpClient, message);
}
catch (System.Net.Mail.SmtpException smtpExc)
{
// Log error to event log using StatusCode information in
// smtpExc.StatusCode
MsgBox((smtpExc.StatusCode.ToString + " ==>Procedure SmtpException"));
}
catch (Exception Exc)
{
// Log error to event log using StatusCode information in
// smtpExc.StatusCode
MsgBox((Exc.Message + " ==>Procedure Exception"));
}
private void CheckExceptionAndResend(System.Net.Mail.SmtpFailedRecipientsException exObj, System.Net.Mail.SmtpClient smtpClient, MailMessage emailMessage)
{
try
{
for (int recipient = 0; (recipient <= (exObj.InnerExceptions.Length - 1)); recipient++)
{
System.Net.Mail.SmtpStatusCode statusCode;
// Each InnerException is an System.Net.Mail.SmtpFailed RecipientException
statusCode = exObj.InnerExceptions(recipient).StatusCode;
if (((statusCode == Net.Mail.SmtpStatusCode.MailboxBusy)
|| (statusCode == Net.Mail.SmtpStatusCode.MailboxUnavailable)))
{
// Log this to event log: recExc.InnerExceptions(recipient).FailedRecipient
System.Threading.Thread.Sleep(5000);
smtpClient.Send(emailMessage);
}
else
{
// Log error to event log.
// recExc.InnerExceptions(recipient).StatusCode or use statusCode
}
}
MsgBox((exObj.Message + " ==>Procedure SmtpFailedRecipientsException"));
}
catch (Exception ex)
{
// At this point we have an non recoverable issue:
// NOTE: At this point we do not want to re-throw the exception because this method
// was called from a 'Catch' block and we do not want a hard error to display to the client.
// Options: log error, report issue to client via msgbox, etc. This is up to you.
// To display issue as you have before:
MsgBox((exObj.Message + " ==>Email was not sent"));
}
}
Such kind of errors have a asnychronous nature. When sending mail you talk to the local smtp server of your provider. That server afterwards starts to deliver the mail to the target mail system.
So the SmtpClient class can only show you errors occuring while talking to your local smtp server.
Typically when an error like "unknown user" occures on the target system, it will send an email with the failure message to the originator email address.
This post is helpful to me.
By the way if you're using .net 4.0 this one will be the changes on the above code. Sorry for my first post i don't know why it appears that way.
Here's the code:
private void CheckExceptionAndResend(System.Net.Mail.SmtpFailedRecipientsException exObj, System.Net.Mail.SmtpClient smtpClient, MailMessage emailMessage)
{
try
{
for (int recipient = 0; (recipient <= (exObj.InnerExceptions.Length - 1)); recipient++)
{
System.Net.Mail.SmtpStatusCode statusCode;
// Each InnerException is an System.Net.Mail.SmtpFailed RecipientException
//for .net 4.0
//statusCode = exObj.InnerExceptions(recipient).StatusCode;
statusCode = exObj.StatusCode;
//if (((statusCode == Net.Mail.SmtpStatusCode.MailboxBusy) || (statusCode == Net.Mail.SmtpStatusCode.MailboxUnavailable)))
//for .net 4.0
if (((statusCode == System.Net.Mail.SmtpStatusCode.MailboxBusy)
|| (statusCode == System.Net.Mail.SmtpStatusCode.MailboxUnavailable)))
{
// Log this to event log: recExc.InnerExceptions(recipient).FailedRecipient
System.Threading.Thread.Sleep(5000);
smtpClient.Send(emailMessage);
}
else
{
// Log error to event log.
// recExc.InnerExceptions(recipient).StatusCode or use statusCode
}
}
//MsgBox((exObj.Message + " ==>Procedure SmtpFailedRecipientsException"));
}
catch (Exception ex)
{
// At this point we have an non recoverable issue:
// NOTE: At this point we do not want to re-throw the exception because this method
// was called from a 'Catch' block and we do not want a hard error to display to the client.
// Options: log error, report issue to client via msgbox, etc. This is up to you.
// To display issue as you have before:
// MsgBox((exObj.Message + " ==>Email was not sent"));
}
}

Categories