.SendMailAsync() use in MVC - c#

I am trying to send email from my MVC application, it sends fine when I use the .Send() method but takes a while to come back so I wanted to use the .SendMailAsync() function, but I am receiving the following error during execution.
An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle. If this exception occurred while executing a Page, ensure that the Page is marked <%# Page Async="true" %>
This is my code sample. How can I configure this to send using .SendMailAsync()
Email Wrapper Class:
using System.Net.Mail;
namespace Helpers
{
public class Email
{
// constants
private const string HtmlEmailHeader = "<html><head><title></title></head><body style='font-family:arial; font-size:14px;'>";
private const string HtmlEmailFooter = "</body></html>";
// properties
public List<string> To { get; set; }
public List<string> CC { get; set; }
public List<string> BCC { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
// constructor
public Email()
{
To = new List<string>();
CC = new List<string>();
BCC = new List<string>();
}
// send
public void Send()
{
MailMessage message = new MailMessage();
foreach (var x in To)
{
message.To.Add(x);
}
foreach (var x in CC)
{
message.CC.Add(x);
}
foreach (var x in BCC)
{
message.Bcc.Add(x);
}
message.Subject = Subject;
message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter);
message.BodyEncoding = System.Text.Encoding.UTF8;
message.From = new MailAddress(From);
message.SubjectEncoding = System.Text.Encoding.UTF8;
message.IsBodyHtml = true;
SmtpClient client = new SmtpClient("relay.mail.server");
client.SendMailAsync(message);
}
}
}
Controller:
public ActionResult Index()
{
Email email = new Email();
email.To.Add("to#email.com");
email.From = "from#email.com";
email.Subject = "Subject";
email.Body = "<p><strong>Hello</strong></p><p>This is my first Email Message</p>";
email.Send();
}
EDIT
Further to the actual question asked, the underlying issue was the delay created when sending emails. I looked further into the actual issue and with the help of this post:
ASP.Net MVC background threads for email creation and sending
modified my Email Wrapper class to spawn off a new thread to perform the email processing:
using System.Net.Mail;
namespace Helpers
{
public class Email
{
// constants
private const string HtmlEmailHeader = "<html><head><title></title></head><body style='font-family:arial; font-size:14px;'>";
private const string HtmlEmailFooter = "</body></html>";
// properties
public List<string> To { get; set; }
public List<string> CC { get; set; }
public List<string> BCC { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
// constructor
public Email()
{
To = new List<string>();
CC = new List<string>();
BCC = new List<string>();
}
// send
public void Send()
{
MailMessage message = new MailMessage();
foreach (var x in To)
{
message.To.Add(x);
}
foreach (var x in CC)
{
message.CC.Add(x);
}
foreach (var x in BCC)
{
message.Bcc.Add(x);
}
message.Subject = Subject;
message.Body = string.Concat(HtmlEmailHeader, Body, HtmlEmailFooter);
message.BodyEncoding = System.Text.Encoding.UTF8;
message.From = new MailAddress(From);
message.SubjectEncoding = System.Text.Encoding.UTF8;
message.IsBodyHtml = true;
SmtpClient client = new SmtpClient("relay.mail.server");
new Thread(() => { client.Send(message); }).Start();
}
}
}

Admittedly, the error is a bit obtuse, but all it's really telling you is that you're calling an asynchronous method from a synchronous method, which isn't allowed. If you're going to use async, you have to use async all the way up the chain.
So, first you need to change your Send method definition to return a Task:
public async Task Send()
And set your async method call to await:
await client.SendMailAsync(message);
Then, do the same for your action:
public async Task<ActionResult> Index()
And:
await email.Send();
UPDATE
Async doesn't do what I think you think it does. When your action is invoked by a request, it will not return a response until all code inside the action has fully executed. Async is not a magic wand that makes the action return the response quicker. Your task (in this case, sending an email) takes as long as it takes and async or not, the action will not return a response until the task has completed.
So why use async then? Because what async does do is let go the thread from the server pool. Let's say IIS is running in a pretty standard config, you'll likely have somewhere around 1000 threads available. This is often called the "max requests", because typically 1 request == 1 thread. So, if you server comes under heavy load and you're fielding more than the "max requests", each subsequent request is queued until a thread from the pool becomes available again. If all the threads are tied up waiting on something to complete, then your server essentially deadlocks. But, when you use async, you tell IIS essentially, "I'm waiting on something. Here's my thread back, so you can use it to field another request. I'll let you know when I need it back." That allows requests in the queue to proceed.
Long and short, do always use async when you are doing anything that involves waiting, because it allows server resources to be used more efficiently, but remember that it doesn't make things happen quicker.
EDIT 12/11/14 - Updated terminology a bit to make clear that async is only useful when a thread is waiting, not just involved in some long-running task. For example, running complex financial calculations could "take a while", but would not be a good fit for async because all the work is CPU-bound. The task may be long-running, but if the thread is not in a wait-state, it can't be used for other tasks and your async method will essentially just run as sync, but with extra overhead.

This might help you out.
public void Send(MailAddress toAddress, string subject, string body, bool priority)
{
Task.Factory.StartNew(() => SendEmail(toAddress, subject, body, priority), TaskCreationOptions.LongRunning);
}
private void SendEmail(MailAddress toAddress, string subject, string body, bool priority)
{
MailAddress fromAddress = new MailAddress(WebConfigurationManager.AppSettings["SmtpFromAddress"]);
string serverName = WebConfigurationManager.AppSettings["SmtpServerName"];
int port = Convert.ToInt32(WebConfigurationManager.AppSettings["SmtpPort"]);
string userName = WebConfigurationManager.AppSettings["SmtpUserName"];
string password = WebConfigurationManager.AppSettings["SmtpPassword"];
var message = new MailMessage(fromAddress, toAddress);
message.Subject = subject;
message.Body = body;
message.IsBodyHtml = true;
message.HeadersEncoding = Encoding.UTF8;
message.SubjectEncoding = Encoding.UTF8;
message.BodyEncoding = Encoding.UTF8;
if (priority) message.Priority = MailPriority.High;
Thread.Sleep(1000);
SmtpClient client = new SmtpClient(serverName, port);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.EnableSsl = Convert.ToBoolean(WebConfigurationManager.AppSettings["SmtpSsl"]);
client.UseDefaultCredentials = false;
NetworkCredential smtpUserInfo = new NetworkCredential(userName, password);
client.Credentials = smtpUserInfo;
client.Send(message);
client.Dispose();
message.Dispose();
}
The Thread.Sleep is there because this will send mail through so fast that many SMTP servers will report too many emails from same IP error message. Although ASP.NET handles asynchronous send mail, it will not send more than one message at a time. It waits until callback occurs before sending another email. This approach will send messages in parallel as fast as the code can call Send().

I think the below does what you're trying to accomplish:
Modify your controller as below:
public async Task<ActionResult> Index()
{
Email email = new Email();
email.SendAsync();
}
And in your Email class add the SendAsync method as below
public async Task SendAsync()
{
await Task.Run(() => this.send());
}
The action will return before the email is sent and the request will not be blocked.
Try to see the behaviour with default mvc template with this code:
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
LongRunningTaskAsync();
return View();
}
public static async Task LongRunningTaskAsync()
{
await Task.Run(() => LongRunningTask());
}
public static void LongRunningTask()
{
Debug.WriteLine("LongRunningTask started");
Thread.Sleep(10000);
Debug.WriteLine("LongRunningTask completed");
}
The login page will be displayed instantly. But output window will display "LongRunningTask completed" 10 seconds later.

Related

How can I adapt this code to use a single instance instead of creating multiple instances of a service?

I am writing a service for sending emails and I would like to send multiple email notifications at the same time. What I currently have is this:
private void SendInstantMailNotification(string notificationId)
{
MailMessage? message = null;
var notifications = _dbContext
.Notifications
.Where(x => x.Id.Equals(notificationId))
.ToList();
var notification = notifications.First();
message = notification.Content;
Smtp.SendMailSync(message, SmtpConfiguration, Smtp.MailTypeEnum.HTML);
}
The last line of the code creates an instance of the "SMTP" service. And for each time I would like to send an email a new instance is created.
How do I achieve this to be only one instance to be created and called multiple times without overloading the system?
This is the constructor:
private readonly NotificationQueueContext _dbContext;
protected NotificationQueueService(NotificationQueueContext dbContext)
{
_dbContext = dbContext;
}
As I understand, you need a mechanism to sequentially run some tasks. So I created a Background service which creates a SMTP client once and a ConcurrentQueue to hold the mail requests and run them one by one.
This service is going to be active through the whole process of your application so it has while(TRUE) in it. and after each email it sends it waits for 500 ms.
If you want to send a mail from other services you just need to call RegisterMailRequest to enqueue a mail request.
you should define this service as a HostedService like this:
services.AddHostedService<SequentialJobHandler>();
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using Microsoft.Extensions.Hosting;
using MimeKit;
namespace Sample
{
public class SequentialJobHandler : BackgroundService
{
private readonly string MailServerAddress;
private readonly int MailServerPort;
private readonly string AdminEmailAccount;
private readonly string AdminEmailAccountPass;
private readonly string MailUser;
private readonly string MailTitle;
private ConcurrentQueue<MailRequest> queue = new ConcurrentQueue<MailRequest>();
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (var client = new SmtpClient())
{
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
await client.ConnectAsync(MailServerAddress, MailServerPort, MailKit.Security.SecureSocketOptions.Auto);
// Note: only needed if the SMTP server requires authentication
await client.AuthenticateAsync(MailUser, AdminEmailAccountPass);
while (true)
{
MailRequest localValue = null;
if (queue.TryDequeue(out localValue))
{
if (localValue != null)
{
SendMail(localValue, client);
}
}
Thread.Sleep(500);
}
//await client.DisconnectAsync(true);
}
}
private async Task SendMail(MailRequest request, SmtpClient client)
{
var message = new MimeMessage();
message.From.Add(new MailboxAddress(MailTitle, AdminEmailAccount));
message.To.Add(new MailboxAddress(request.toUsername, request.toEmail));
message.Subject = request.subject;
message.Body = new TextPart("html")
{
Text = request.body
};
await client.SendAsync(message);
}
public void RegisterMailRequest(MailRequest request)
{
queue.Enqueue(request);
}
public class MailRequest
{
public string toUsername, toEmail, subject, body;
}
}
}
hope this helps.

ASP.NET MVC Usermanager SendEmailAsync: How to change sender email

I am using ASP.NET MVC5 with Identity 2, theres a file called IdentityConfig.cs which has EmailSerive that I have implemented like this:
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
using (var client = new SmtpClient())
{
using (var mailMessage = new MailMessage("info#mydomain.com", message.Destination, message.Subject, message.Body))
{
mailMessage.IsBodyHtml = true;
await client.SendMailAsync(mailMessage);
}
}
}
}
With this setup, I am able to send user emails by calling this method on UserManager:
await UserManager.SendEmailAsync(user.Id, emailSubject, emailBody);
So far so good, but Id want the email to be sent from different senders depending on the subject. For instance, account registration/reset password will have sender account#mydomain.com, information inquiry emails will have sender info#mydomain.com, sales/order placement should have sender sales#mydomain.com.
Unfortunately, the method SendEmailAsync has no way to set the sender, and I have no idea how to achieve this in the EmailService either. Can anyone help with this problem? Is there a way to add an extension method to UserManager or EmailSerivce so I can optionally specify a different sender?
If I am in your place I would put all my emails in the web.config file and access it in a private method in the EmailService class that returns relevant email based on the subject and then call this method at the place of email parameter. e.g:
public async Task SendAsync(IdentityMessage message)
{
using (var client = new SmtpClient())
{
//calling getEmail() instead of email
using (var mailMessage = new MailMessage(getEmail(message.Subject),
message.Destination, message.Subject, message.Body))
{
mailMessage.IsBodyHtml = true;
await client.SendMailAsync(mailMessage);
}
}
}
private string getEmail(string subject)
{
var emails = ConfigurationManager.AppSettings["Emails"];
string[] emailAddresses = emails.Split(',');
//your logic here
return email;
}

ASP.NET Identity UserManager IIdentityMessageService pass extra parameters to SendAsync

I've successfully created custom email service provider for UserManager by extending IIdentityMessageService:
public class ExchangeEmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
using (var client = new SmtpClient())
{
client.Host = "mail.example.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(#"noreply", "P#ssw0rd");
var from = new MailAddress("no-reply#example.com");
var to = new MailAddress(message.Destination);
var mailMessage = new MailMessage(from, to)
{
Subject = message.Subject,
Body = message.Body,
IsBodyHtml = true
};
await client.SendMailAsync(mailMessage);
}
}
}
Inside UserManager's Create I'm creating new instance of my service and assigning it to EmailService:
userManager.EmailService = new ExchangeEmailService();
This all works fine, but I have requirement to send emails from different email addresses, based on place this is called from, unfortunately UserManager.SendEmailAsync isn't allowing me to pass anything except userId, subject and body.
Ideally I'd like to be able to specify email address when calling SendEmailAcync or enum value.
I've searched in UserManager source and I thought about creating custom SendEmailAsync implementation, but I'd have to change multiple places - IIdentityMessageService, UserManager.
I know I can create new interface and build my email sending class on it, but I'd like to keep changes to minimum.
What I'd like to get:
I'd like to have enum containing names of mailboxes:
public enum Mailboxes
{
Noreply = 0,
Service = 1,
Contact = 2
}
and be able to call SendEmailAsync with that additional parameter:
await UserManager.SendEmailAsync(user.Id, "Account created", "Nice email content.", Mailboxes.Noreply );
I'm aware that I can set different title and use same email address, but this is requirement I got.
How can I do that?
A hack would be to create a model to store what you want sent to the service
public class MessageBody {
public string Body { get; set; }
public MailBoxes Source { get; set; }
}
and then serialize it (JSON maybe) as the body parameter of the SendEmailAsync method call.
var message = new MessageBody {
Body = "Nice email content.",
Source = Mailboxes.Noreply
};
var body = JsonConvert.SerializeObject(message);
await UserManager.SendEmailAsync(user.Id, "Account created", body);
The service would then deserialize the model, construct the email with the custom info and then send the email.
public class ExchangeEmailService : IIdentityMessageService {
readonly IMailBoxProvider provider;
public ExchangeEmailService(IMailBoxProvider provider) {
this.provider = provider;
}
public async Task SendAsync(IdentityMessage message) {
using (var client = new SmtpClient()) {
client.Host = "mail.example.com";
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(#"noreply", "P#ssw0rd");
//Get the body and from address
var fromEmailAddress = "default-email#example.com";
var body = message.Body;
try {
var msg = JsonConvert.DeserializeObject<MessageBody>(body);
if(msg != null) {
body = msg.Body;
fromEmailAddress = provider.GetMailbox(msg.Source);
}
} catch { }
var from = new MailAddress(fromEmailAddress);
var to = new MailAddress(message.Destination);
var mailMessage = new MailMessage(from, to)
{
Subject = message.Subject,
Body = body,
IsBodyHtml = true
};
await client.SendMailAsync(mailMessage);
}
}
}
And just make sure to assigning it to UserManager.EmailService:
userManager.EmailService = new ExchangeEmailService(new MailBoxProvider());

Why do we periodically get "An asynchronous call is already in progress" when calling SmtpClient.Send?

We have some (synchronous) email code, which creates a class that creates an SmtpClient, and then sends an email. The SmtpClient is not reused; however we get the following exception every now and then:
System.Web.HttpUnhandledException (0x80004005): Exception of type 'System.Web.HttpUnhandledException' was thrown. ---> System.InvalidOperationException: An asynchronous call is already in progress. It must be completed or canceled before you can call this method.
at System.Net.Mail.SmtpClient.Send(MailMessage message)
at EmailSender.SendMail(MailAddress fromMailAddress, string to, String subject, String body, Boolean highPriority) in ...\EmailSender.cs:line 143
Code looks like this:
// ...
var emailSender = new EmailSender();
emailSender.SendMail(toEmail, subject, body, true);
// emailSender not used past this point
// ...
public class EmailSender : IEmailSender
{
private readonly SmtpClient smtp;
public EmailSender()
{
smtp = new SmtpClient();
}
public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
{
if (fromMailAddress == null)
throw new Exception();
if (to == null)
throw new ArgumentException("No valid recipients were supplied.", "to");
// Mail initialization
var mailMsg = new MailMessage
{
From = fromMailAddress,
Subject = subject,
Body = body,
IsBodyHtml = true,
Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
};
mailMsg.To.Add(to);
smtp.Send(mailMsg);
}
}
You need to dispose of the SmtpClient using Dispose, using or by implementing the disposable pattern for your class EmailSender (which more appropriate here because you are tying the lifetime of the SmtpClient to the lifetime of the EmailSender in the constructor.)
That might solve this exception.
My guess is that the SmtpClient was not designed to send multiple messages concurrently.
I would change the class like this instead:
public class EmailSender : IEmailSender
{
Queue<MailMessage> _messages = new Queue<MailMessage>();
SmtpClient _client = new SmtpClient();
public EmailSender()
{
}
public void SendMail(MailAddress fromMailAddress, string to, string subject, string body, bool highPriority)
{
if (fromMailAddress == null)
throw new ArgumentNullException("fromMailAddress");
if (to == null)
throw new ArgumentException("No valid recipients were supplied.", "to");
// Mail initialization
var mailMsg = new MailMessage
{
From = fromMailAddress,
Subject = subject,
Body = body,
IsBodyHtml = true,
Priority = (highPriority) ? MailPriority.High : MailPriority.Normal
};
mailMsg.To.Add(to);
lock (_messages)
{
_messages.Enqueue(mailMsg);
if (_messages.Count == 1)
{
ThreadPool.QueueUserWorkItem(SendEmailInternal);
}
}
}
protected virtual void SendEmailInternal(object state)
{
while (true)
{
MailMessage msg;
lock (_messages)
{
if (_messages.Count == 0)
return;
msg = _messages.Dequeue();
}
_client.Send(msg)
}
}
}
As there are really no reason to create the client in the constructor.
I also changed so that the class throws ArgumentNullException and not Exception if fromMailAddress is null. An empty Exception doesn't say much..
Update
The code do now use a thread pool thread for the sending (and reusing the smtpclient).

Creating a Mailer and Mail classes. Need advice on Disposing

I need to create an System.Net.Mail wrapper that I could use with IOC and in a Fluent way.
The idea would be to have two classes, Mailer and Mail, used as follows:
IMailer mailer = new Mailer();
IMail mail = new Mail()
.Attach("Photo", fileData, "image/jpg")
.Body("This is the photo I forgot to send")
.From("from#xyz.com")
.Subject("The missing photo")
.To("to#xyz.com");
mailer.New(mail).Send();
Or in a completely Fluent way something like:
new Mailer()
.New()
.Attach("Photo", fileData, "image/jpg")
.Body("This is the photo I forgot to send")
.From("from#xyz.com")
.Subject("The missing photo")
.To("to#xyz.com")
.Done()
.Send();
I have done most of the work but I am having a problems with disposing. The mailer class:
public interface IMailer : IDisposable {
IMailer New(IMail mail);
IMail New();
void Cancel();
void Send();
void SendAsync(SendCompletedEventHandler callback, Object token = null);
} // IMailer
public class Mailer : IMailer {
private SmtpClient _client;
private IMail _mail;
public Mailer() {
_client = new SmtpClient();
} // Mailer
public IMailer New(IMail mail) {
_mail = mail;
return this;
} // New
public IMail New() {
_mail = new Mail(this);
return _mail;
} // New
public void Cancel() {
_client.SendAsyncCancel();
} // Cancel
public void Send() {
Send(null, null);
} // Send
public void SendAsync(SendCompletedEventHandler callback, Object token = null) {
Send(callback, token);
} // SendAsync
private void Send(SendCompletedEventHandler callback = null, Object token = null) {
using (MailMessage message = new MailMessage()) {
message.From = new MailAddress(_mail.Data.From);
message.Subject = _mail.Data.Subject;
_mail.Data.To.ForEach(x => message.To.Add(new MailAddress(x)));
message.Body = _mail.Data.Text;
_mail.Data.Attachments.ForEach(x => { message.Attachments.Add(new Attachment(new MemoryStream(x.Value.Item2), x.Key, x.Value.Item1)); });
if (callback == null)
_client.Send(message);
else {
_client.SendCompleted += callback;
_client.SendAsync(message, token);
}
};
} // Send
public void Dispose() {
Dispose(true);
} // Dispose
protected virtual void Dispose(Boolean disposing) {
if (disposing) {
if (_client != null)
_client.Dispose();
}
} // Dispose
} // Mailer
And the mail class which contains the mail data and builds a mail is as follows:
public interface IMail {
MailData Data { get; }
IMail Attach(String name, Byte[] file, String mime);
IMail Body(String text);
IMail From(String contact);
IMail Subject(String subject);
IMail To(String contact);
IMailer Done();
} // IMail
public class Mail : IMail {
private IMailer _mailer;
public MailData Data { get; private set; }
public Mail() {
Data = new MailData();
} // Mail
public Mail(IMailer mailer) {
Data = new MailData();
_mailer = mailer;
} // Mail
public IMail Attach(String name, Byte[] file, String mime) {
Tuple<String, Byte[]> attachment;
if (!Data.Attachments.TryGetValue(name, out attachment))
Data.Attachments.Add(name, new Tuple<String, Byte[]>(mime, file));
return this;
} // Attach
public IMail Body(String text) {
Data.Text = text;
return this;
} // Body
public IMail From(String contact) {
Data.From = contact;
return this;
} // From
public IMail Subject(String subject) {
Data.Subject = subject;
return this;
} // Subject
public IMail To(String contact) {
Data.To.Add(contact);
return this;
} // To
public IMailer Done() {
return _mailer;
} // Done
} // Mail
public class MailData {
public Dictionary<String, Tuple<String, Byte[]>> Attachments { get; set; }
public String From { get; set; }
public String Subject { get; set; }
public String Text { get; set; }
public HashSet<String> To { get; set; }
public MailData() {
Attachments = new Dictionary<String, Tuple<String, Byte[]>>();
To = new HashSet<String>();
} // MailData
} // MailData
The mailer uses a MailMessage which is disposed just after sending the email.
When I dispose the mailer then the SMTP Client is also disposed ...
However, I am not sure how to dispose the Mail itself.
This would be important to clear all those Attachements Files in the MailData dictionary.
I should be able to do it in two ways:
IMail mail = new Mail()
.Attach("Photo", fileData, "image/jpg")
// Define other properties of mail
// Send mail using mailer
mail.Dispose(); // Dispose mail
Or when completely fluently:
mailer
.New()
.Attach("Photo", fileData, "image/jpg")
// Other properties of mail
.Done()
.Send()
.Dispose();
This would dispose the mailer and its mail ... Or:
mailer
.New()
.Attach("Photo", fileData, "image/jpg")
// Other properties of mail
.Done()
.Send()
.Clear;
This would dispose the Mail associated to the mailer but not the mailer so I can send another mail with the same mailer.
Or any other configuration that you might suggest.
I am not sure the best way to go ...
Any advice is welcome.
Thank You,
Miguel
NOTE: when using IOC I will inject the IMailer in my services ...
The only unmanaged resource I see in your code is SmtpClient. So only IMailer should be disposed. Mail and MailData are all managed so no need to dispose them. So you only have to call mailer.Dispose() and that's it.
(Btw it would be a good idea to use Mailer inside a using block.)
EDIT:
I've just noticed that you also use SendAsnyc which makes disposing a bit trickier. When sending asynchronously you should call dispose in the callback. So your private Send method seems problematic as well because it disposes the MailMessage object (with the using block) but SendAsync might still need it.
See also:
What are best practices for using SmtpClient, SendAsync and Dispose under .NET 4.0
How to dispose objects having asynchronous methods called?
I'd create the class as a one-off; that is, one instance can send one email and then you throw it away. In this way you can properly dispose both the IMail and IMailer implementations when you call Send. You can configure your IoC to always return a new unique instance when you ask for IMailer.
This design has the added benefit that people using your interface won't forgot to dispose it.
Alternately, you could stick to the standard using block pattern, although it always seems odd to me to dispose something a container handed out.

Categories