Introduction
I was working on a web application; one of the requirements was to send bulk mails to multiple users.
Earlier, I was using SMTP client object to send the mail. But when the number of recipients increased, the application was stuck for a long time. Also the same SMTP server was used among multiple applications, so load on Server was very high.
For performance optimization, I created a new queue using MSMQ on a separate server where all the mails are queued and then created one application which would read emails one by one and send it via SMTP client. Now my original web application is not directly connecting to SMTP server, due to which wait time for sending bulk mail will be very low and load on SMTP server will be less.
Background
MSMQ is essentially a messaging protocol that allows applications running on separate servers or processes(s) to communicate in a failsafe manner. A queue is a temporary storage location from which messages can be sent and received reliably. They can be used for communication across heterogeneous networks and between computers which may not always be connected. Steps to create MSMQ – Queue manually are as follows:
- Right click on “My Computer”
- Click on “Manage”
- Expand Services And Application
- Now you can see the “Message Queuing”
- Expand “Private Queue”, click on “New Private queue” – Enter the name “emailqueue”
This will create a new Queue, please refer to the attached document for details. Now we will see how to create it programmatically.
Please note: Namespaces required are: ‘System.Messaging
’ for MSMQ and ‘System.Net.Mail
' for sending mail using SMTP client. Also MSMQ must be installed on your machine.
Using the Code
Now we will see how to use MSMQ to send mail programmatically. I have divided the application in two parts:
- Application which will create MSMQ queue and hold all mail - information as an object
- Application which will read mail info from Queue and send email via SMTP.
Part 1
First we will look into the Email Sender part: Here, I have created Windows form which will take inputs from user like “To Address”, “From Address”,”Subject”,”Body”, etc. On the “Send Button” click, call method QueueMessage
from EmailService
.
QueueMessage
is used to create the transactional Queue, if it does not exist, it also pushes the data inside this queue.
string msmqQueuePath = @".\Private$\EmailQueue";
Message msmqMsg = new Message();
msmqMsg.Body = emailMessage;
msmqMsg.Recoverable = true;
msmqMsg.Formatter = new BinaryMessageFormatter();
MessageQueue msmqQueue = new MessageQueue();
if (!MessageQueue.Exists(msmqQueuePath))
{
msmqQueue = MessageQueue.Create(msmqQueuePath);
}
msmqQueue.Formatter = new BinaryMessageFormatter();
msmqQueue.Send(msmqMsg);
Part 2
Email Receiver:
Here, I have created a console application, which will be running continuously to read any new message.
We need an instance of message queue, which will read the email message object from specified path. Please note, Message path in receiver must be the same as sender. Then raise receive complete event of message queue.
as: _msmqQueue.ReceiveCompleted+= In msmqQueue_ReceiveCompleted event :
extract the actual message like: (EmailEntities.EmailMessage)e.Message.Body.
Now create the instance of “MailMessage
” and set the appropriate properties. Create instance of SMTP client and call Send
method. This will send the actual message from the queue.
string messageQueuePath = @".\private$\EmailQueue";
_msmqQueue = new MessageQueue(messageQueuePath);
_msmqQueue.Formatter = new BinaryMessageFormatter();
_msmqQueue.MessageReadPropertyFilter.SetAll();
_msmqQueue.ReceiveCompleted+=new ReceiveCompletedEventHandler(msmqQueue_ReceiveCompleted);
_msmqQueue.BeginReceive();
EmailEntities.EmailMessage emailMsg = (EmailEntities.EmailMessage)e.Message.Body;
MailMessage mailMesage = new MailMessage();
mailMesage.Subject = emailMsg.Subject;
mailMesage.Body = emailMsg.Body;
SmtpClient oclient = new SmtpClient();
oclient.Send(mailMesage);
Points of Interest
Some readers asked me how we can add attachments in this mail? As per analysis, we cannot directly serialize the attachment, but there are alternatives:
- We can create a separate class which will help to serialize the entire attachment: something like:
class SerializeableAttachment
{ String ContentId; SerializeableContentDisposition ContentDisposition;
SerializeableContentType ContentType; .....
- Instead of serializing the entire attachment, just store that attachment in a database with key value pair and add corresponding property in Send email class. In receiver part, read that key for attachment and get actual attachment from db.
For more information, refer to this link.
History
- Initial version
- Minor changes
- Minor code changes