private void ProcessQueueMessage(CloudQueueMessage msg)
        {
            // Log and delete if this is a "poison" queue message (repeatedly processed
            // and always causes an error that prevents processing from completing).
            // Production applications should move the "poison" message to a "dead message"
            // queue for analysis rather than deleting the message.           
            if (msg.DequeueCount > 5)
            {
                Trace.TraceError("Deleting poison message:    message {0} Role Instance {1}.",
                    msg.ToString(), GetRoleInstance());
                sendEmailQueue.DeleteMessage(msg);
                return;
            }
            // Parse message retrieved from queue.
            // Example:  2012-01-01,[email protected],0
            var messageParts = msg.AsString.Split(new char[] { ',' });
            var partitionKey = messageParts[0];
            var rowKey = messageParts[1];
            var restartFlag = messageParts[2];
            Trace.TraceInformation("ProcessQueueMessage start:  partitionKey {0} rowKey {1} Role Instance {2}.",
                partitionKey, rowKey, GetRoleInstance());
            // If this is a restart, verify that the email hasn't already been sent.
            if (restartFlag == "1")
            {
                var retrieveOperationForRestart = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey);
                var retrievedResultForRestart = messagearchiveTable.Execute(retrieveOperationForRestart);
                var messagearchiveRow = retrievedResultForRestart.Result as SendEmail;
                if (messagearchiveRow != null)
                {
                    // SendEmail row is in archive, so email is already sent. 
                    // If there's a SendEmail Row in message table, delete it,
                    // and delete the queue message.
                    Trace.TraceInformation("Email already sent: partitionKey=" + partitionKey + " rowKey= " + rowKey);
                    var deleteOperation = TableOperation.Delete(new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" });
                    try
                    {
                        messageTable.Execute(deleteOperation);
                    }
                    catch
                    {
                    }
                    sendEmailQueue.DeleteMessage(msg);
                    return;
                }
            }
            // Get the row in the Message table that has data we need to send the email.
            var retrieveOperation = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey);
            var retrievedResult = messageTable.Execute(retrieveOperation);
            var emailRowInMessageTable = retrievedResult.Result as SendEmail;
            if (emailRowInMessageTable == null)
            {
                Trace.TraceError("SendEmail row not found:  partitionKey {0} rowKey {1} Role Instance {2}.",
                    partitionKey, rowKey, GetRoleInstance());
                return;
            }
            // Derive blob names from the MessageRef.
            var htmlMessageBodyRef = emailRowInMessageTable.MessageRef + ".htm";
            var textMessageBodyRef = emailRowInMessageTable.MessageRef + ".txt";
            // If the email hasn't already been sent, send email and archive the table row.
            if (emailRowInMessageTable.EmailSent != true)
            {
                SendEmailToList(emailRowInMessageTable, htmlMessageBodyRef, textMessageBodyRef);

                var emailRowToDelete = new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" };
                emailRowInMessageTable.EmailSent = true;

                var upsertOperation = TableOperation.InsertOrReplace(emailRowInMessageTable);
                messagearchiveTable.Execute(upsertOperation);
                var deleteOperation = TableOperation.Delete(emailRowToDelete);
                messageTable.Execute(deleteOperation);
            }

            // Delete the queue message.
            sendEmailQueue.DeleteMessage(msg);

            Trace.TraceInformation("ProcessQueueMessage complete:  partitionKey {0} rowKey {1} Role Instance {2}.",
               partitionKey, rowKey, GetRoleInstance());
        }
 private string GetPlainTextUnsubscribeLink(SendEmail emailRowInMessageTable)
 {
     return String.Format("\nCopy the following URL into your browser unsubscribe.\n{0}/unsubscribe?id={1}&listName={2}",
         RoleEnvironment.GetConfigurationSettingValue("AzureMailServiceURL"),
         emailRowInMessageTable.SubscriberGUID,
         emailRowInMessageTable.ListName);
 }
 private string GetHTMLUnsubscribeLink(SendEmail emailRowInMessageTable)
 {
     return String.Format("<p>Click the following link to unsubscribe. <a href=\"{0}/unsubscribe?id={1}&listName={2}\">Unsubscribe</a></p>",
         RoleEnvironment.GetConfigurationSettingValue("AzureMailServiceURL"),
         emailRowInMessageTable.SubscriberGUID,
         emailRowInMessageTable.ListName);
 }
 private void SendEmailToList(SendEmail emailRowInMessageTable, string htmlMessageBodyRef, string textMessageBodyRef)
 {
     var email = SendGrid.GetInstance();
     email.From = new MailAddress(emailRowInMessageTable.FromEmailAddress);
     email.AddTo(emailRowInMessageTable.EmailAddress);
     email.Html = GetBlobText(htmlMessageBodyRef) + GetHTMLUnsubscribeLink(emailRowInMessageTable);
     email.Text = GetBlobText(textMessageBodyRef) + GetPlainTextUnsubscribeLink(emailRowInMessageTable);
     email.Subject = emailRowInMessageTable.SubjectLine;
     var credentials = new NetworkCredential(RoleEnvironment.GetConfigurationSettingValue("SendGridUserName"),
         RoleEnvironment.GetConfigurationSettingValue("SendGridPassword"));
     var transportREST = Web.GetInstance(credentials);
     transportREST.Deliver(email);
 }
        private void ProcessQueueMessage(CloudQueueMessage msg)
        {
            Stopwatch requestTimer = Stopwatch.StartNew();
            var request = RequestTelemetryHelper.StartNewRequest("ProcessEmailQueueMessage", DateTimeOffset.UtcNow);
            CallContext.LogicalSetData(CORRELATION_SLOT, request.Id);
            //Thread.SetData(Thread.GetNamedDataSlot(CORRELATION_SLOT), request.Id);
            try
            {
                // Log and delete if this is a "poison" queue message (repeatedly processed
                // and always causes an error that prevents processing from completing).
                // Production applications should move the "poison" message to a "dead message"
                // queue for analysis rather than deleting the message.           
                if (msg.DequeueCount > 5)
                {
                    Trace.TraceError("Deleting poison message:    message {0} Role Instance {1}.",
                        msg.ToString(), GetRoleInstance());
                    sendEmailQueue.DeleteMessage(msg);
                    request.Properties.Add(new KeyValuePair<string,string>("FailureCode","PoisonMessage"));
                    request.Properties.Add(new KeyValuePair<string, string>("DequeueCount", msg.DequeueCount.ToString()));
                    if (msg.InsertionTime != null)
                    {
                        request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds));
                    }
                    RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false);
                    return;
                }
                // Parse message retrieved from queue.
                // Example:  2012-01-01,[email protected],0
                var messageParts = msg.AsString.Split(new char[] { ',' });
                var partitionKey = messageParts[0];
                var rowKey = messageParts[1];
                var restartFlag = messageParts[2];
                Trace.TraceInformation("ProcessQueueMessage start:  partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance());
                // If this is a restart, verify that the email hasn't already been sent.
                if (restartFlag == "1")
                {
                    var retrieveOperationForRestart = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey);
                    var retrievedResultForRestart = messagearchiveTable.Execute(retrieveOperationForRestart);
                    var messagearchiveRow = retrievedResultForRestart.Result as SendEmail;
                    if (messagearchiveRow != null)
                    {
                        // SendEmail row is in archive, so email is already sent. 
                        // If there's a SendEmail Row in message table, delete it,
                        // and delete the queue message.
                        Trace.TraceInformation("Email already sent: partitionKey=" + partitionKey + " rowKey= " + rowKey);
                        var deleteOperation = TableOperation.Delete(new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" });
                        try
                        {
                            messageTable.Execute(deleteOperation);
                        }
                        catch(Exception ex)
                        {
                            aiClient.TrackException(ex);
                        }
                        sendEmailQueue.DeleteMessage(msg);
                        request.Properties.Add(new KeyValuePair<string, string>("SuccessCode", "NoOp-MessageAlreadySent"));
                        if (msg.InsertionTime != null)
                        {
                            request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds));
                        }
                        RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, true);
                        return;
                    }
                }
                // Get the row in the Message table that has data we need to send the email.
                var retrieveOperation = TableOperation.Retrieve<SendEmail>(partitionKey, rowKey);
                var retrievedResult = messageTable.Execute(retrieveOperation);
                var emailRowInMessageTable = retrievedResult.Result as SendEmail;
                if (emailRowInMessageTable == null)
                {
                    Trace.TraceError("SendEmail row not found:  partitionKey {0} rowKey {1} Role Instance {2}.",partitionKey, rowKey, GetRoleInstance());
                    request.Properties.Add(new KeyValuePair<string, string>("FailureCode", "SendEmailRowNotFound"));
                    if (msg.InsertionTime != null)
                    {
                        request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds));
                    }
                    RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false);
                    return;
                }
                // Derive blob names from the MessageRef.
                var htmlMessageBodyRef = emailRowInMessageTable.MessageRef + ".htm";
                var textMessageBodyRef = emailRowInMessageTable.MessageRef + ".txt";
                // If the email hasn't already been sent, send email and archive the table row.
                if (emailRowInMessageTable.EmailSent != true)
                {
                    SendEmailToList(emailRowInMessageTable, htmlMessageBodyRef, textMessageBodyRef);

                    var emailRowToDelete = new SendEmail { PartitionKey = partitionKey, RowKey = rowKey, ETag = "*" };
                    emailRowInMessageTable.EmailSent = true;

                    var upsertOperation = TableOperation.InsertOrReplace(emailRowInMessageTable);
                    messagearchiveTable.Execute(upsertOperation);
                    var deleteOperation = TableOperation.Delete(emailRowToDelete);
                    messageTable.Execute(deleteOperation);
                }

                // Delete the queue message.
                sendEmailQueue.DeleteMessage(msg);
                Trace.TraceInformation("ProcessQueueMessage complete:  partitionKey {0} rowKey {1} Role Instance {2}.", partitionKey, rowKey, GetRoleInstance());
                request.Properties.Add(new KeyValuePair<string, string>("SuccessCode", "EmailSent"));
                if (msg.InsertionTime != null)
                {
                    request.Metrics.Add(new KeyValuePair<string, double>("EmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds));
                }
                RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, true);
            }
            catch (Exception ex)
            {
                request.Properties.Add(new KeyValuePair<string, string>("FailureCode", "Exception"));
                if (msg.InsertionTime != null)
                {
                    request.Metrics.Add(new KeyValuePair<string, double>("FailedEmailProcessingTimeMs", ((TimeSpan)(DateTimeOffset.Now - msg.InsertionTime)).Milliseconds));
                }
                RequestTelemetryHelper.DispatchRequest(request, requestTimer.Elapsed, false);
                throw ex;
            }            
        }
        private void ProcessMessage(Message messageToProcess, string restartFlag)
        {

            // Get Mailing List info to get the "From" email address.
            var retrieveOperation = TableOperation.Retrieve<MailingList>(messageToProcess.ListName, "mailinglist");
            var retrievedResult = mailingListTable.Execute(retrieveOperation);
            var mailingList = retrievedResult.Result as MailingList;
            if (mailingList == null)
            {
                Trace.TraceError("Mailing list not found: " + messageToProcess.ListName + " for message: " + messageToProcess.MessageRef);
                return;
            }
            // Get email addresses for this Mailing List.
            string filter = TableQuery.CombineFilters(
               TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, messageToProcess.ListName),
               TableOperators.And,
               TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.NotEqual, "mailinglist"));
            var query = new TableQuery<Subscriber>().Where(filter);
            var subscribers = mailingListTable.ExecuteQuery(query).ToList();
            aiClient.TrackMetric("SubscriberCount", subscribers.Count); 
            foreach (Subscriber subscriber in subscribers)
            {
                // Verify that the subscriber email address has been verified.
                if (subscriber.Verified == false)
                {
                    Trace.TraceInformation("Subscriber " + subscriber.EmailAddress + " not Verified, so not queuing ");
                    continue;
                }

                // Create a SendEmail entity for this email.              
                var sendEmailRow = new SendEmail
                {
                    PartitionKey = messageToProcess.PartitionKey,
                    RowKey = messageToProcess.MessageRef.ToString() + subscriber.EmailAddress,
                    EmailAddress = subscriber.EmailAddress,
                    EmailSent = false,
                    MessageRef = messageToProcess.MessageRef,
                    ScheduledDate = messageToProcess.ScheduledDate,
                    FromEmailAddress = mailingList.FromEmailAddress,
                    SubjectLine = messageToProcess.SubjectLine,
                    SubscriberGUID = subscriber.SubscriberGUID,
                    ListName = mailingList.ListName
                };

                // When we try to add the entity to the SendEmail table, 
                // an exception might happen if this worker role went 
                // down after processing some of the email addresses and then restarted.
                // In that case the row might already be present, so we do an Upsert operation.
                try
                {
                    var upsertOperation = TableOperation.InsertOrReplace(sendEmailRow);
                    messageTable.Execute(upsertOperation);
                }
                catch (Exception ex)
                {
                    string err = "Error creating SendEmail row:  " + ex.Message;
                    if (ex.InnerException != null)
                    {
                        err += " Inner Exception: " + ex.InnerException;
                    }
                    Trace.TraceError(err, ex);
                }

                // Create the queue message.
                string queueMessageString =
                    sendEmailRow.PartitionKey + "," +
                    sendEmailRow.RowKey + "," +
                    restartFlag;
                var queueMessage = new CloudQueueMessage(queueMessageString);
                sendEmailQueue.AddMessage(queueMessage);
            }

            Trace.TraceInformation("ProcessMessage end PK: "
                + messageToProcess.PartitionKey);
        }