Sending HTML emails with ASP.NET MVC2 and MVCContrib

March 14, 2010 21:45
Recently I was playing with ASP.NET MVC2 trying to find a neat solution for sending emails. This post is a sum-up of that little endeavour.

From it you will learn
  1. How to send HTML emails with MVCContrib and its EmailTemplateService
  2. How to test email sender without SMTP server
  3. How to use Ninject with MVC controllers

Send HTML emails with ASP.NET MVCAll those impatient readers out there, go ahead and download the source code of a small app that will be crafted in the rest of this post (zip comes along with MVCContrib source code, for some rich debugging experience).


How to send HTML emails with MVCContrib.


Email template service in MVCContrib (inspired by Castle MonoRail) is a fantastic feature
. Usage is pretty simple:

var emailService = new MvcContrib.Services.EmailTemplateService();
var email = emailService.RenderMessage(controllerContext, viewName);
smtpClient.Send(email);

You provide a specially formatted view and the service generates a MailMessage ready to be sent out. Here's an example of how the view should look like:


from: <%= ViewData["from"] %>
to: <%= ViewData["to"] %>
subject: <%= ViewData["subject"] %>
<html>
    <body>
        Here goes the email text
    </body>
</html>


How to test email sender without SMTP server.

Contrary to all expectations, you don't really need an SMTP server for testing. There's a config option that makes your SMTP client saving emails to a local folder, instead of sending them out.

Put this in your web.config and enjoy:


<system.net>
    <mailSettings>
        <smtp deliveryMethod="SpecifiedPickupDirectory">
            <specifiedPickupDirectory pickupDirectoryLocation="c:\Temp\" />
        </smtp>
    </mailSettings>
</system.net>


How to use Ninject with MVC controllers.

Ninject comes along with a neat Ninject.Mvc project for MVC development.

With that guy, DI in controllers becomes superlatively easy. Just
inherit your application from NinjectHttpApplication and add this code:


protected override void OnApplicationStarted()
{
    RegisterRoutes(RouteTable.Routes); //your route registration
 
    //allow DI for all controllers in this assembly
    RegisterAllControllersIn(Assembly.GetExecutingAssembly());  
}
 
protected override IKernel CreateKernel()
{
    //define Ninjection rules in a separate module
    return new StandardKernel(new DefaultBindingsModule());
}
 
private class DefaultBindingsModule : NinjectModule
{
    public override void Load()
    {
        //controllers that use IEmailService, will get EmailService instances
        Bind<IEmailService>().To<EmailService>();
    }
}

And voila, your controllers will magically get instantiated with instances of EmailService - assuming you define controllers like that:

public class FooController : Controller
{
    private readonly IEmailService _emailService;
    public FooController(IEmailService emailService)
    {
        _emailService = emailService;
    }
    ...
}


Project structure.

I recommend the following project structure:

Provide a base class
EmailModel with common To, From, Subject etc properties. Derived classes will expose specific properties to substitute placeholders in concrete emails.

In a similar fashion, define
Email.Master with common metadata ("to", "from", "subject" etc) and basic markup for the emails. Concrete email templates would have to deal only with their specific details.

Disclaimers and concerns.


MVCContrib uses regular expressions to retrieve "to", "from" and other metadata. A somewhat more robust and maintainable idea would be specifying that metadata in xml:
 

<metadata 
    isHtmlEmail="true"
    subject="Hello, MVCContrib!"
    to="one@test.com, two@test.com"
    from="sender@test.com"
    cc="cc@test.com"
    bcc="bcc@test.com"
    ...
 />
<html>
  <body>
    Email text
  </body>
</html>

Another concern is that while it uses ViewEngine.FindView() along with IView.Render() to render the view on the fly - but it does not call ViewEngine.ReleaseView afterwards, so the email view isn't disposed properly.

Other than that, MVCContrib and its
EmailTemplateService is a great approach for sending emails. Try it yourself!

kick it on DotNetKicks.com


Comments

Comments are closed