Image by Sergio Quesada | Some Rights Reserved
This is the second part of an article demonstrating how to build out an application for sending personalized email to recipients selected from a list. In the first part, we put together the basic structure of our ASP.NET MVC application, according to a simple list of requirements.
Now, we will add the email functionality, such that the user may select one or more recipients from a list using checkboxes, then generate and send a personalize email to each.
Review Part I –> Send Email to Selected Recipients from your ASP.NET MVC Web Application
- Source Code on Github
- The Problem to Solve
- Pseudo-Code the New and Improved SendMail Method
- Retrieve the Selected Recipients
- Abstracting an Email Message – the Message Class
- The Mail Sender Class
- Mail Client Configuration Settings
- Walking Through the Execution of the Send Mail Method
- Additional Resources and Items of Interest
The Send Mail Method
In the previous post, we created a stub for the SendMail
method and threw some code in there to emulate a long-running process such as sending a list of emails:
The Send Mail Method Stub:
[HttpPost] [Authorize] public ActionResult SendMail(MailRecipientsViewModel recipients) { // Mail-sending code will happen here . . . System.Threading.Thread.Sleep(2000); return RedirectToAction("Index"); }
We did this so that we could run our application, and all the front end functionality would work as expected. Now let’s see what we need to do in order to actually send some mail.
The Problem to Solve
Let’s take a look at what we need to accomplish here by breaking the problem into steps. In order to send a personalized message to each of the recipients selected by the user, we need to:
- Retrieve the recipient data for each of the recipients selected by the user
- Compose a message personalized for each recipient by inserting the recipient’s name into some sort of message template, and addressed to the recipient’s email.
- Retrieve the current User’s email address to use as the “From” email address, and the current user’s name to use in the signature
- Represent the above as a “Message” which can be aggregated into a list of messages to be sent, addressed to each recipient.
- Add a record to the
SentMail
table representing the key points for each email sent (basically a log) - When sending is complete, redirect to the Index page, and refresh, displaying updated records which include a filed for the date mail was most recently sent to each recipient.
- Pass the list to some sort of Mail Sender, which can iterate over the list and send each message.
Pseudo-Code the New and Improved SendMail Method
Given the above, it looks like we might want our SendMail method to do something like this:
Pseudo-Code for Steps in Sending Mail:
[HttpPost] [Authorize] public ActionResult SendMail(MailRecipientsViewModel recipients) { // Retrieve the ids of the recipients selected: // Grab the recipient records: // Build the message container for each: // Send the mail: // Save a record of each mail sent: // Reload the index form: }
So, lets make that happen!
In order to keep our Action method clean and simple, we are going to make each of these steps a call to a locally defined method. The code for each step could then also be easily moved out of the controller into another class or classes, depending on the needs of your application and/or fussiness about how much work should be done within the controller itself. We aren’t going to get all pedantic about it here.
Retrieve the Selected Recipients
We can start by thinking about what we actually receive in the recipients argument passed to SendMail
from the HTTP request body. We will get back an instance of MailRecipientsViewModel
, which provides a method getSelectedRecipientIds()
. This returns an IEnumerable<int>
representing the Ids of the recipients selected by the user on our form.
Reviewing our MailRecipientsViewModel
class:
The Get Selected Recipient Ids Method:
public class MailRecipientsViewModel { public List<SelectRecipientEditorViewModel> MailRecipients { get; set; } public MailRecipientsViewModel() { this.MailRecipients = new List<SelectRecipientEditorViewModel>(); } public IEnumerable<int> getSelectedRecipientIds() { return (from r in this.MailRecipients where r.Selected select r.MailRecipientId).ToList(); } }
Private Implementation Code
Now That we have our Ids, lets fill in the rest of our private helper methods. Add the following code the the controller after the SendMail
stub:
Adding Code to the Controller to Implement the Send Mail Method:
IEnumerable<MailRecipient> LoadRecipientsFromIds(IEnumerable<int> selectedIds) { var selectedMailRecipients = from r in db.MailRecipients where selectedIds.Contains(r.MailRecipientId) select r; return selectedMailRecipients; } IEnumerable<Message> createRecipientMailMessages( IEnumerable<MailRecipient> selectedMailRecipients) { var messageContainers = new List<Message>(); var currentUser = db.Users.Find(User.Identity.GetUserId()); foreach (var recipient in selectedMailRecipients) { var msg = new Message() { Recipient = recipient, User = currentUser, Subject = string.Format("Welcome, {0}", recipient.FullName), MessageBody = this.getMessageText(recipient, currentUser) }; messageContainers.Add(msg); } return messageContainers; } void SaveSentMail(IEnumerable<SentMail> sentMessages) { foreach (var sent in sentMessages) { db.SentMails.Add(sent); db.SaveChanges(); } } string getMessageText(MailRecipient recipient, ApplicationUser user) { return "" + string.Format("Dear {0}, ", recipient.FullName) + Environment.NewLine + "Thank you for your interest in our latest product. " + "Please feel free to contact me for more information!" + Environment.NewLine + Environment.NewLine + "Sincerely, " + Environment.NewLine + string.Format("{0} {1}", user.FirstName, user.LastName); }
Abstracting an Email Message – the Message Class
In the code above, we see we create an instance of a class Message
. This is another Model we need to add to our Models folder. We are using the Message
class to represent everything needed to send an email:
Add the following class to the Models folder:
The Message Class:
public class Message { public MailRecipient Recipient { get; set; } public ApplicationUser User { get; set; } public string Subject { get; set; } public string MessageBody { get; set; } }
Also, in the createRecipientMailMessages
method, we grab the current logged-in User with the following call:
Get the Current Logged-in User:
var currentUser = db.Users.Find(User.Identity.GetUserId());
In order for this to work we need to add a reference to the Microsoft.AspNet.Identity
namespace in the usings
at the top of our code file, or this code won’t work.
Call Implementation Code from Send Mail Method
Now that we have broken out each of our steps into discrete private method calls, we can call these from within the SendMail method:
[HttpPost] [Authorize] public ActionResult SendMail(MailRecipientsViewModel recipients) { // Retrieve the ids of the recipients selected: var selectedIds = recipients.getSelectedRecipientIds(); // Grab the recipient records: var selectedMailRecipients = this.LoadRecipientsFromIds(selectedIds); // Build the message container for each: var messageContainers = this.createRecipientMailMessages(selectedMailRecipients); // Send the mail: var sender = new MailSender(); var sent = sender.SendMail(messageContainers); // Save a record of each mail sent: this.SaveSentMail(sent); // Reload the index form: return RedirectToAction("Index"); }
In the above, we have working code for everything except step 4, in which we initialize an instance of MailSender
, and then actually send the mail. Now we get to the nitty-gritty of our application.
The Mail Sender Class
In our SendMail
code, we build up a list of Message
instances, which we then pass to a new class we haven’t looked at yet – the MailSender class.
Add a new class to the project, name it MailSender
, and paste in the following code:
The Mail Sender Class:
public class MailSender { public IEnumerable<SentMail> SendMail(IEnumerable<Message> mailMessages) { var output = new List<SentMail>(); // Modify this to suit your business case: string mailUser = "youremail@outlook.com"; string mailUserPwd = "password"; SmtpClient client = new SmtpClient("smtp.host.com"); client.Port = 587; client.DeliveryMethod = SmtpDeliveryMethod.Network; client.UseDefaultCredentials = false; System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(mailUser, mailUserPwd); client.EnableSsl = true; client.Credentials = credentials; foreach (var msg in mailMessages) { var mail = new MailMessage(msg.User.Email.Trim(), msg.Recipient.Email.Trim()); mail.Subject = msg.Subject; mail.Body = msg.MessageBody; try { client.Send(mail); var sentMessage = new SentMail() { MailRecipientId = msg.Recipient.MailRecipientId, SentToMail = msg.Recipient.Email, SentFromMail = msg.User.Email, SentDate = DateTime.Now }; output.Add(sentMessage); } catch (Exception ex) { throw ex; // Or, more likely, do some logging or something } } return output; } }
You will need to make sure you import the following namespaces for the code to work:
Required Namespaces for the Mail Sender Class:
using AspNetEmailExample.Models; using System; using System.Collections.Generic; using System.Net.Mail;
Mail Client Configuration Settings
I discuss the details of setting up the mail client for Outlook.com or Gmail in another post. For most mail hosts, the client configuration should resemble the above. However, pay attention. For one, as discussed in the post linked above, if you have some sort of two-step authorization in place on your mail host, you will likely need to use an Application-Specific Password for this to work. Also note, you can send mail using your Outlook.com account as a host, but unlike most other mail hosting accounts, the Outlook.com host name for SMTP is:
smtp-mail.outlook.com
Whereas Gmail is simply:
smtp.gmail.com
For other mail hosts, you may have to experiment a little, or consult the provider documentation.
Walking Through the Execution of the Send Mail Method
With all of our pieces in place, we can now walk through the execution of SendMail()
and take an high-level look at what is going on in all these small, refactored methods, and how they align with the steps we defined to send mail to each recipient,
First, we use our list of selected Ids to retrieve a corresponding list of fully instantiated recipient instances. This list is then returned to the call in SentMail
, whereupon it is passed to the createMailRecipientMessages()
method.
This next method iterates the list of recipients, and creates a new Message
instance for each, supplying the property values needed to send an email. Two of these, the User
and MessageBody
properties, involve additional calls. Retrieving the current user requires a call into the Microsoft.AspNet.Identity
library.
The getMessageText
method, from which we retrieve the actual text for each mail message, represents a crude, “just make it work” implementation of what, in a real application, should probably be a template-based system. I have kept things simple here, but in reality we would probably like to be able to retrieve a message template from some resource or another, and populate the template properly from code without having to re-write and recompile.
How you implement this would depend significantly on your application requirements and is beyond the scope of this article (this article is already long, considering the topic is not all that advanced!). If you have either questions, or brilliant ideas for implementing such a system in your own application, I would love to hear either. This might become the topic of another article.
Once we have constructed our list of Message objects, we pass that to the MailSender.SendMail
method, and, well, send the damn mail. We can see that each Message
object is used to create a System.Net.Mail.MailMessage
object, which is then sent using our properly configured mail client.
Once each Message
is sent, we create a SentMail
object, and then return the list of List<SentMail>
back to the SendMail
controller method, at which point we persist the SentMail
objects, and redirect back to the Index
method.
Running the Project and Sending Mail
Now, we likely have our test data from before, when we entered some examples to test out our front-end. You may want to go ahead and change the example.com email addresses to an actual mail account you can access, to ensure all is working properly. Then, run the application, log in, and try the “Email Selected” button again. you may want to deselect one or two of the potential recipients in the list, just to see the difference:
Try Sending Some Mail:
This time, we should see our “Busy” spinner for a moment, and then be redirected back to a refreshed Index view, now updated with the last date we sent mail to the selected recipients:
The Updated Index View After Sending Mail:
As we can see, the two items selected for sending email have now been updated with a Last Sent date.
What Went Wrong?
If you have been following along, building this out as you go, and something doesn’t work, I strongly recommend cloning the example project from source and trying to run that. For what appears to be a simple application, there are actually a lot of places where I may have missed some small but critical item in posting the code here on the blog. I’ve tried to balance providing everything you need to build this out yourself with keeping the article length manageable (and still semi-failed on the length part!).
If you clone from source, and still have an issue, please do describe it in the comments section and/or shoot me an email. Also, if you see somewhere I have made a mistake, or taken the “dumb way” to doing something, I am ALL EARS.
I’ve tried to combine providing a useful tutorial on the mechanics of sending mail from an application, with my own steps in thinking through the problem. Obviously, some of the content here is aimed at folks new to ASP.NET and/or Web Development in general.
Thanks for reading, and your feedback is always appreciated.
Additional Resources and Items of Interest
- Get the source code from Github
- PART I: Send Email to Selected Recipients from your ASP.NET MVC Web Application
- Extending Identity Accounts and Implementing Role-Based Authentication in ASP.NET MVC 5
- Configuring Db Connection and Code-First Migration for Identity Accounts in ASP.NET MVC 5 and Visual Studio 2013
- ASP.NET MVC Display an HTML Table with Checkboxes to Select Row Items
- ASP.NET MVC: Show Busy Indicator on Form Submit using JQuery and Ajax
- C# – Generate and Deliver PDF Files On-Demand from a Template Using iTextSharp
Comments
Soumik
AuthorSupposed I did pagination on that table that you created(Per page got 5 rows). First I selected 2 email from 1st page after move to next page from same table select another 3 email. So, can I send total 5 mail 2 from 1st page and 3 from second page(after move to 2nd page ,1st page rows still selected)?
Example: Like google mail,I selected 3 of mail then I go to second page and also select another 3 mail. When I delete total 6 mail deleted..how to do that?
Arif
AuthorHi,
I have a requirement where in a MVC application one user should be able to email another user and the data saved to the database. If its a read email the flag should be updated to read from unread. How do I do that, sorry I am new to VS.
Thanks,
Arif
T
AuthorThank you so much,
I was able to use your example to tailor it to my own use. Very useful.
I greatly appreciate this article and your time.
T
AuthorGreat post.
This is exactly what I need.
Thank yoU!
John Atten
AuthorGreat! Thanks for taking a moment to comment! Cheers