Wednesday, 20 November 2013

How to Send an Email with Verification link to user in Asp.net after registration

Introduction: In this post I will try to explain how we can send the verification Email with link to user after registration in Asp.net.
Send an Email with Verification link

Description:
In the previous article i have explained  How to Send Email with Attachment in Asp.net ,How to send Email in Asp.net using Web.config.
Here I created a table USER_REGISTRATION. USER_ID is primary key.
USER_ID
int
USERNAME
varchar(50)
FIRST_NAME
varchar(50)
LAST_NAME
varchar(50)
EMAIL
varchar(50)
IS_APPROVE
bit
PASSWORD
varchar(50)

Add two webforms to project User_registration.aspx and Verification.aspx.
Drag and drop the controls from Toolbox to .aspx page.
  <table border="1px solid" width="330px">
    <tr><b style="color:Blue;">Send Verification link to User after Registartion</b></tr>
    <tr><td>Username:</td><td>
        <asp:TextBox ID="txtusername" runat="server"></asp:TextBox></td></tr>
    <tr><td>First Name:</td><td>
        <asp:TextBox ID="txtfirst" runat="server"></asp:TextBox></td></tr>
    <tr><td>Last Name:</td><td>
        <asp:TextBox ID="txtlast" runat="server"></asp:TextBox></td></tr>
    <tr><td>Email:</td><td>
        <asp:TextBox ID="txtemail" runat="server"></asp:TextBox></td></tr>
    <tr><td>Password:</td><td>
        <asp:TextBox ID="txtpassword" runat="server" TextMode="Password"></asp:TextBox></td></tr>
    <tr><td>Confirm Password:</td><td>
        <asp:TextBox ID="txtconfirm" runat="server" TextMode="Password"></asp:TextBox></td></tr>
    <tr><td>&nbsp;</td><td>
        <asp:Button ID="Button1" runat="server" Text="Register" OnClientClick="return ValidateEmail();"
            onclick="Button1_Click" /></td></tr>
    </table>

Add the below given Javascript between Head Tag to validate the Email Textbox:
<script language="javascript" type="text/javascript">
         function ValidateEmail() {
             var emailRegex = new RegExp(/^([\w\.\-]+)@([\w\-]+)((\.(\w){2,3})+)$/i);
             var emailAddress = document.getElementById("<%= txtemail.ClientID %>").value;
             var valid = emailRegex.test(emailAddress);
             if (!valid) {
                 alert("Please Enter Valid Email address");
                 return false;
             } else
                 return true;
         }
  </script>

Now go to .aspx.cs page and write the below mention code:
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Net.Mail;
using System.Net;

SqlConnection con = newSqlConnection(ConfigurationManager.ConnectionStrings["con"].ToString()); 
    protected void Page_Load(object sender, EventArgs e)
    {
        if (con.State == ConnectionState.Closed)
        {
            con.Open();
        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        if (IsUsernameEmailExist())
        {
           
            Messagebox("Username/Email address already exists. Please try another");
            return;
        }
        if (txtpassword.Text == txtconfirm.Text)
        {
            SqlCommand cmd = new SqlCommand("insert into USER_REGISTRATION (USERNAME,FIRST_NAME,LAST_NAME,EMAIL,PASSWORD) values(@USERNAME,@FIRST_NAME,@LAST_NAME,@EMAIL,@PASSWORD)", con);
            cmd.Parameters.AddWithValue("@USERNAME", txtusername.Text);
            cmd.Parameters.AddWithValue("@FIRST_NAME", txtfirst.Text);
            cmd.Parameters.AddWithValue("@LAST_NAME", txtlast.Text);
            cmd.Parameters.AddWithValue("@EMAIL", txtemail.Text);
            cmd.Parameters.AddWithValue("@PASSWORD", txtpassword.Text);
            cmd.ExecuteNonQuery();
            Sendemail();
            Clear();
            Messagebox("User Register Successfully");
            cmd.Dispose();
      
        }
        else
        {
            Messagebox("Passowrd Not Match");
        }                 
    }

    public void Sendemail()
    {
        string ActivationUrl;
        try
        {
            MailMessage message = new MailMessage();
            message.From = new MailAddress("demo@gmail.com", "Saklani");
            message.To.Add(txtemail.Text);
            message.Subject = "Verification Email";
            ActivationUrl = Server.HtmlEncode("http://localhost:9525/Important_Testing/Verification.aspx?USER_ID=" + GetUserID(txtemail.Text));
            message.Body = "<a href='"+ActivationUrl+"'>Click Here to verify your acount</a>";
            message.IsBodyHtml = true;
            SmtpClient smtp = new SmtpClient();
            smtp.Host = "smtp.gmail.com";
            smtp.Port = 587;
            smtp.Credentials = new System.Net.NetworkCredential("demo@gmail.com","demo123");
            smtp.EnableSsl = true;
            smtp.Send(message);
        }
        catch (Exception ex)
        {
        }
    }
    private string GetUserID(string Email)
    {
        SqlCommand cmd = new SqlCommand("SELECT USER_ID FROM USER_REGISTRATION WHERE EMAIL=@EMAIL", con);     
        cmd.Parameters.AddWithValue("@EMAIL", txtemail.Text);
        string UserID = cmd.ExecuteScalar().ToString();
        return UserID;
    }

    private bool IsUsernameEmailExist()
    {
        SqlCommand cmd = new SqlCommand("Select * from USER_REGISTRATION where USERNAME='" + txtusername.Text + "' or EMAIL='" + txtemail.Text + "'", con);
        cmd.ExecuteNonQuery();
        DataTable dt = new DataTable();
        SqlDataAdapter adp = new SqlDataAdapter(cmd);
        adp.Fill(dt);
        if (dt.Rows.Count > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    private void Messagebox(string Message)
    {
        Label lblMessageBox = new Label();

        lblMessageBox.Text =
            "<script language='javascript'>" + Environment.NewLine +
            "window.alert('" + Message + "')</script>";

        Page.Controls.Add(lblMessageBox);

    }
  public void Clear()
    {
        txtusername.Text = "";
        txtfirst.Text = "";
        txtlast.Text = "";
        txtemail.Text = "";
        txtpassword.Text = "";
        txtconfirm.Text = "";
    }

In VB (.aspx.vb)

Imports System.Data
Imports System.Data.SqlClient
Imports System.Configuration
Imports System.Net.Mail

Dim con As New SqlConnection(ConfigurationManager.ConnectionStrings("con").ToString())

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) HandlesMe.Load
        If con.State = ConnectionState.Closed Then
            con.Open()
        End If
    End Sub
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) HandlesButton1.Click
        If IsUsernameEmailExist() Then

            Messagebox("Username/Email address already exists. Please try another")
            Return
        End If
        If txtpassword.Text = txtconfirm.Text Then
            Dim cmd As New SqlCommand("insert into USER_REGISTRATION (USERNAME,FIRST_NAME,LAST_NAME,EMAIL,PASSWORD) values(@USERNAME,@FIRST_NAME,@LAST_NAME,@EMAIL,@PASSWORD)", con)
            cmd.Parameters.AddWithValue("@USERNAME", txtusername.Text)
            cmd.Parameters.AddWithValue("@FIRST_NAME", txtfirst.Text)
            cmd.Parameters.AddWithValue("@LAST_NAME", txtlast.Text)
            cmd.Parameters.AddWithValue("@EMAIL", txtemail.Text)
            cmd.Parameters.AddWithValue("@PASSWORD", txtpassword.Text)
            cmd.ExecuteNonQuery()
            Sendemail()
            Messagebox("User Register Successfully")

            cmd.Dispose()
        Else
            Messagebox("Passowrd Not Match")
        End If
    End Sub
    Private Function IsUsernameEmailExist() As Boolean
        Dim cmd As New SqlCommand(("Select * from USER_REGISTRATION where USERNAME='" + txtusername.Text & "' or EMAIL='") + txtemail.Text & "'", con)
        cmd.ExecuteNonQuery()
        Dim dt As New DataTable()
        Dim adp As New SqlDataAdapter(cmd)
        adp.Fill(dt)
        If dt.Rows.Count > 0 Then
            Return True
        Else
            Return False
        End If
    End Function
    Private Sub Messagebox(ByVal Message As String)
        Dim lblMessageBox As New Label()

        lblMessageBox.Text = "<script language='javascript'>" + Environment.NewLine &"window.alert('" & Message & "')</script>"

        Page.Controls.Add(lblMessageBox)

    End Sub

 
    Public Sub Sendemail()
        Dim ActivationUrl As String
        Try
            Dim message As New MailMessage()
            message.From = New MailAddress("demo@gmail.com", "Saklani")
            message.[To].Add(txtemail.Text)
            message.Subject = "Verification Email"
            ActivationUrl = Server.HtmlEncode("http://localhost:9525/Important_Testing/Verification.aspx?USER_ID=" & GetUserID(txtemail.Text))
            message.Body = "<a href='" & ActivationUrl & "'>Click Here to verify your acount</a>"
            message.IsBodyHtml = True
            Dim smtp As New SmtpClient()
            smtp.Host = "smtp.gmail.com"
            smtp.Port = 587
            smtp.Credentials = New System.Net.NetworkCredential("demo@gmail.com", "demo123")
            smtp.EnableSsl = True
            smtp.Send(message)
        Catch ex As Exception
        End Try
    End Sub
    Private Function GetUserID(ByVal Email As String) As String
        Dim cmd As New SqlCommand("SELECT USER_ID FROM USER_REGISTRATION WHERE EMAIL=@EMAIL", con)
        cmd.Parameters.AddWithValue("@EMAIL", txtemail.Text)
        Dim UserID As String = cmd.ExecuteScalar().ToString()
        Return UserID
    End Function


After that now check the QueryString value on Verification.aspx page. Write the below given code on .aspx.cspage:
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

SqlConnection con = newSqlConnection(ConfigurationManager.ConnectionStrings["con"].ToString());
    string USERID, USERNAME;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Request.QueryString["USER_ID"] != null)
        {
            USERID = Request.QueryString["USER_ID"];
            SqlCommand cmd = new SqlCommand("Update USER_REGISTRATION set IS_APPROVE=1 where USER_ID=@USER_ID", con);
            cmd.Parameters.AddWithValue("@USER_ID", USERID);          
            con.Open();
            cmd.ExecuteNonQuery();
            Response.Write(Request.QueryString["USER_ID"]);
        }
    }

In VB (.aspx.vb)

Imports System.Data
Imports System.Data.SqlClient

Private con As New SqlConnection(ConfigurationManager.ConnectionStrings("con").ToString())
    Private USERID As String, USERNAME As String
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) HandlesMe.Load
        If Request.QueryString("USER_ID") IsNot Nothing Then
            USERID = Request.QueryString("USER_ID")
            Dim cmd As New SqlCommand("Update USER_REGISTRATION set IS_APPROVE=1 where USER_ID=@USER_ID", con)
            cmd.Parameters.AddWithValue("@USER_ID", USERID)
            con.Open()
            cmd.ExecuteNonQuery()
            Response.Write(Request.QueryString("USER_ID"))
        End If
    End Sub

Now run the project and check the result.

Wednesday, 13 November 2013

http://codeoverload.wordpress.com/2011/05/22/dropdown-lists-in-mvc-3/

Dropdown Lists in MVC 3

I’ve been lucky enough to work full-time with ASP.NET MVC 3 for the past couple of months, and I’ve completely fallen in love. It’s quite a change from WebForms, and it took a while to ‘click’ so I’m putting together a few examples both as a personal reference, but also to help anyone looking to take the leap from WebForms.
The first thing I struggled with was how to implement dropdown lists, so this post will walk through the steps required to make a simple form:
The first step is to build a model for the form. We need two properties for the dropdown, one to hold the list of options and the other for the ID of the selected option:
public class CreditCardModel
{
    public List<SelectListItem> CardTypeOptions { get; set; }

    [Display(Name = "Card Type")]
    public string CardTypeID { get; set; }

    [Display(Name = "Card Number")]
    [Required(ErrorMessage = "Please provide your card number")]
    public string CardNumber { get; set; }
}
The controller class requires two action methods, one to show the initial form (the GET request) and one to handle the form submission (the POST request):
public class CreditCardController : Controller
{
    public ActionResult AddCard()
    {
        var model = new CreditCardModel();
        
        // Populate the dropdown options
        model.CardTypeOptions = GetCardTypes("MS"); // Set the default to American Express
        return View(model);
    }
    
    [HttpPost]
    public ActionResult AddCard(CreditCardModel model)
    {
        // TODO - Handle the form submit

        // Populate the dropdown options
        model.CardTypeOptions = GetCardTypes("MS"); // Set the default to American Express
        return View(model);
    }
    
    // TODO - AddCardComplete goes here
    
    // TODO - GetCardTypes goes here
}
I’ve hard-coded the dropdown values inside the controller to keep things simple:
private List<SelectListItem> GetCardTypes(string defaultValue)
{
    List<SelectListItem> items = new List<SelectListItem>();
    items.Add(new SelectListItem { Text = "American Express", Value = "AE", Selected = (defaultValue == "AE") });
    items.Add(new SelectListItem { Text = "Mastercard", Value = "MS", Selected = (defaultValue == "MS") });
    items.Add(new SelectListItem { Text = "Visa", Value = "VS", Selected = (defaultValue == "VS") });
    return items;
}
The view needs the ‘@model’ directive at the top to bind to the CreditCardModel. I’m using Razor as my view engine of choice – quite simply, it rocks! Note the use of ValidationSummary, any validation errors will automatically appear here:
@model Demo.Models.CreditCardModel
@{
    ViewBag.Title = "Add Card";
}
<h2>Add Card</h2>
@using (Html.BeginForm())
{
    @Html.ValidationSummary()
<table>
    <tr>
        <td>@Html.LabelFor(x => x.CardTypeID)</td>
        <td>@Html.DropDownListFor(x => x.CardTypeID, Model.CardTypeOptions)</td>
    </tr>
    <tr>
        <td>@Html.LabelFor(x => x.CardNumber)</td>
        <td>@Html.EditorFor(x => x.CardNumber)</td>
    </tr>
</table>
<input type="submit" value="Add Card" />
}
Inside the AddCard POST action we need a check that the model is valid before we save the card details. This can be checked by calling ModelState.IsValid which is available to all action methods.
Something to watch out for – it’s a recommended practice to redirect the user to an action following a post. The reason for this is to prevent the user accidentally re-submitting the form by refreshing the page. This is done by returning a call to RedirectToAction(), passing in the action name.
if (ModelState.IsValid)
{
    // TODO - Save the data to your database...

    // Prevent the user resubmitting the card by asking their
    // browser to request a different page using a GET request
    return RedirectToAction("addcardcomplete");
}
Create a new view called AddCardComplete:
@{
    ViewBag.Title = "Add Card Complete";
}
<h2>Add Card Complete</h2>
<p>Your card has been added</p>
Create an action method for the view inside the controller:
public ActionResult AddCardComplete()
{
    return View();
}
All done! The selected value of the dropdown will be automatically bound to the CardTypeID property of the CreditCardModel instance.

Monday, 11 November 2013

Introduction

MvcMailer provides you with an ActionMailer style email sending NuGet Package for ASP.Net MVC 3/4. So, you can produce professional looking emails composed of your MVC master pages and views with ViewBag.

Why MvcMailer?

Because you want to:
  • Write clean code to send emails instead of spaghetti code
  • Reuse the power of master pages, views and data
  • Easily write unit test for email sending code
  • Send multi-part emails
  • Do some or all the above painlessly.
Now that you are convinced, please install the package!

Install MvcMailer NuGet Package

Open your package manager and type:
PM> install-package MvcMailer

Scaffold Your Mailer

Scaffold is your friend that produces your mailer with master pages and views. Run the following command in your Package Manager console:
PM> Scaffold Mailer.Razor UserMailer Welcome,PasswordReset
You should see the following:
Added MyScaffolder output 'Mailers\IUserMailer.cs'
Added MyScaffolder output 'Mailers\UserMailer.cs'
Added MyScaffolder output 'Views\UserMailer\_Layout.cshtml'
Added MyScaffolder output 'Views\UserMailer\Welcome.cshtml'
Added MyScaffolder output 'Views\UserMailer\PasswordReset.cshtml'
Mailers\IUserMailer.cs defines your mailer interface with two methods - Welcome and PasswordReset. Mailers\UserMailer.cs is your Mailer class that implements IUserMailer and extends MailerBase. MailerBase extends ControllerBase so that your mailer is just like your controller.
Your already got meaningful code in UserMailer. For example, take a look at the following Welcome() method:
public virtual MvcMailMessage Welcome()
{
	//ViewBag.Data = someObject;
	return Populate(x => {
              x.ViewName = "Welcome";
              x.To.Add("some-email@example.com");
              x.Subject = "Welcome";
        });

}
Typically, you will edit this method body and pass models to your views, as you do in your controllers. You can use ViewData, ViewBag and strongly typed Models with your views and master pages. Views\UserMailer contains your master page and email views.
However, if you need more mailers, just use the scaffold command as follows:
PM> Scaffold Mailer.Razor CommentMailer CommentPosted,Liked
PM> Scaffold Mailer.Razor ReportMailer ReportProduced,ReportSent,ReportLoading
When you install MvcMailer it automatically sets the Scaffolder called Mailer to one of Mailer.Razor or Mailer.Aspx depending on the project files. So, if you are using Aspx/Razor view engine, your scaffolder will produce aspx and razor views respectively.
When scaffolding, you can provide a switch -NoInterface if you don't like the interface for your mailers. For example the following one will not create IMyMailer,
PM> Scaffold Mailer.Razor MyMailer Welcome -NoInterface

Configure SMTP Client

MvcMailer already added the following smtp configuration section in your Web.config file. Open web.config and you will see the following:
<!-- Method#1: Configure smtp server credentials -->
<smtp from="some-email@gmail.com">
     <network enableSsl="true" host="smtp.gmail.com" port="587" userName="some-email@gmail.com" password="valid-password" />
</smtp>
You can use the credentials from a Gmail account or use your favorite SMTP client - just specify the values in the config file and you are good to go.
In case you want to drop the emails in a local folder, just uncomment Method 2 and comment out method 1.
<!-- Method#2: Dump emails to a local directory -->      
<smtp from="some-email@gmail.com" deliveryMethod="SpecifiedPickupDirectory">
     <specifiedPickupDirectory pickupDirectoryLocation="<email_directory_path>"/>
</smtp>      
In case you are new to the .Net Mail library, this configuration is used by the System.Net.Mail and there is nothing specific to MvcMailer.

Send Email

Edit Your Mailer

You will edit your mailer method and pass useful data to views. For example, you can do the following:
public virtual MvcMailMessage Welcome()
{
	ViewBag.Name = "Sohan";
	return Populate(x =>{
          x.viewName = "Welcome";
          x.To.Add("sohan39@example.com");
        });
}

Pass Data to Mailer Views

From your Mailer, you can use the following ways to pass data to view:
  • Using ViewBag
ViewBag.Name = "Sohan";
ViewBag.Comment = myComment;
  • Using ViewData
ViewData["Name"] = "Sohan";
ViewData["Comment"] = myComment;
  • Using Strongly Typed Model
var comment = new Comment {From = me, To = you, Message = "Great Work!"};
ViewData = new ViewDataDictionary(comment);   
  • Using Strongly Typed Model with default HtmlHelper use @Html.DisplayFor(model => model.Property) in your view
var comment = new Comment {From = me, To = you, Message = "Great Work!"};
ViewData.Model = comment 

Edit Your Master Page

Edit Views/UserMailer/_Layout.cshtml to write your email master page.
<html>
	<head></head>
	<body>
          <h1>MvcMailer</h1>
          @RenderBody()
          <br />
          ---
          <br />
          Thank you!
	</body>
</html>

Edit Your View

Edit Views/UserMailer/Welcome.cshtml to write your email content that goes inside the master page:
Hello @ViewBag.Name:<br />
Welcome to MvcMailer and enjoy your time!<br />

Support for ASPX Views

MvcMailer scaffolder defaults to your project's preferred view engine. So, if you're using ASPX, you will see .Master and .ASPX views instead of the .cshtml ones - but the flavor should be same!

Forcing the Scaffolder to use Razor or ASPX

# This will set Mailer scaffolder to Razor
PM> Set-DefaultScaffolder -Name Mailer -Scaffolder Mailer.Razor

# This will set Mailer scaffolder to Aspx
PM> Set-DefaultScaffolder -Name Mailer -Scaffolder Mailer.Aspx
If you do not intend to change your default Mailer, you can invoke one of the following to produce your desired view files:
# This will produce Razor views
PM> Scaffold Mailer.Razor UserMailer Welcome,GoodBye

# This will produce Aspx views
PM> Scaffold Mailer.Aspx UserMailer Welcome,GoodBye

Absolute URL Using Url.Abs

Unlike the relative URLs in your web app, your email recipients will need to get absolute URLs for links and images. You can use the Url.Abs extension method from MvcMailer as shown below:
Please <a href="@Url.Abs(Url.Action("Index", "Home"))">Visit Us</a> to find more.

Send Mail from Controller

Add a Reference to IUserMailer
using System.Web;
using System.Web.Mvc;
using MvcMailer_Example.Mailers;
using Mvc.Mailer;

namespace MvcMailer_Example.Controllers
{
    public class HomeController : Controller
    {
        private IUserMailer _userMailer = new UserMailer();
        public IUserMailer UserMailer
        {
            get{return _userMailer;}
            set{_userMailer = value;}
        }
...
Use the Reference to Send Email:
//This is important, for the Send() extension method
using Mvc.Mailer;
...
public ActionResult SendWelcomeMessage()
{
     UserMailer.Welcome().Send(); //Send() extension method: using Mvc.Mailer
     return RedirectToAction("Index");
}
Now, you know the basics of sending emails using MvcMailer. However, you could get a lot more done with MvcMailer. Keep reading if you are interested!

Unit Test Your Mailers

If you have a mailer method like the following:
public virtual MvcMailMessage Welcome()
{
	ViewBag.Name = "Sohan";
	return Populate(x =>{
          x.Subject = "Welcome to MvcMailer";
	  x.To.Add("sohan39@example.com");
          x.viewName = "Welcome";
        });
}
You can write a unit test code like this:
//Test using NUnit and Moq

using System.Linq;
using NUnit.Framework;
using Moq;
using MvcMailer_Example.Mailers;
using System.Net.Mail;

namespace MvcMailer_Example.Tests.Mailers
{
    [TestFixture]
    public class UserMailerTest
    {
        private Mock<UserMailer> _userMailerMock;
        
        [SetUp]
        public void Setup()
        {
            //setup the mock
            _userMailerMock = new Mock<UserMailer>();
            //CallBase will ensure it calls real implementations other than the mocked out methods
            _userMailerMock.CallBase = true;
        }

        [Test]
        public void Test_WelcomeMessage()
        {
            //Arrange: Moq out the PopulateBody method
            _userMailerMock.Setup(mailer => mailer.PopulateBody(It.IsAny<MailMessage>(), "Welcome", null));

            //Act
            var mailMessage = _userMailerMock.Object.Welcome();

            //Assert
            _userMailerMock.VerifyAll();
            Assert.AreEqual("Sohan", _userMailerMock.Object.ViewBag.Name);
            Assert.AreEqual("Welcome to MvcMailer", mailMessage.Subject);
            Assert.AreEqual("sohan39@example.com", mailMessage.To.First().ToString());
        }

    }
}

Unit Test A Controller That Uses Mailer

Since the scaffold generates the interface for you, its as easy as testing model repositories. Say, you have the following controller action that sends an Email:
public ActionResult SendWelcomeMessage()
{
    UserMailer.Welcome().Send();
    return RedirectToAction("Index");
}
You can write the following test for this:

using System.Net.Mail;
using System.Web.Mvc;
using Moq;
using Mvc.Mailer;
using MvcMailer_Example.Controllers;
using MvcMailer_Example.Mailers;
using NUnit.Framework;

namespace MvcMailer_Example.Tests.Controllers
{
    [TestFixture]
    public class HomeControllerTests
    {
        private Mock<IUserMailer> _userMailerMock;
        private HomeController _homeController;

        [SetUp]
        public void Setup()
        {
            _homeController = new HomeController();

            _userMailerMock = new Mock<IUserMailer>();
            _homeController.UserMailer = _userMailerMock.Object;
            
            MailerBase.IsTestModeEnabled = true;
        }

        [TearDown]
        public void TearDown()
        {
            TestSmtpClient.SentMails.Clear();
        }

        [Test]
        public void Test_SendWelcomeMessage()
        {
            //Arrange
            var mailMessage = new MailMessage();
            _userMailerMock.Setup(userMailer => userMailer.Welcome()).Returns(mailMessage);

            //Act
            var actionResult = _homeController.SendWelcomeMessage();

            //Assert
            _userMailerMock.VerifyAll();
            Assert.AreEqual(1, TestSmtpClient.SentMails.Count);
            Assert.AreEqual(mailMessage, TestSmtpClient.SentMails[0]);

            var routeValues = (actionResult as RedirectToRouteResult).RouteValues;
            Assert.AreEqual(routeValues["action"], "Index");
        }
    }
}

Send Email with Attachments

Just add your attachments to your MailMessage object. For example, you can do the following:
public virtual MvcMailMessage Welcome(string attachmentPath)
{
	return Populate(x => {
          x.viewName = "Welcome";
          x.Attachments.Add(new Attachment(attachmentPath));
        });
}

Send Multi-part Emails

You can send both text/plain and text/html parts for a single email. Just add your text and html views like the following:
Views
|--- UserMailer
     |--- _Layout.cshtml              => email master page for text/html
     |--- Welcome.cshtml              => email content for text/html

     |--- _Layout.text.cshtml         => email master page for text/plain
     |--- Welcome.text.cshtml         => email content for text/plain
MvcMailer will look for both text and html versions. In case it finds both, it will send a multi-part email containing both parts. Otherwise, it will decide based on what is passed to UserMailer.IsBodyHtml property.
You can use the -WithText switch to Scaffold both html and plain text view files using the following.
PM> scaffold Mailer MyMailer Hello -WithText
Added MvcMailer output 'Mailers\IMyMailer.cs'
Added MvcMailer output 'Mailers\MyMailer.cs'
Added MyScaffolder output 'Views\MyMailer\_Layout.cshtml'
Added MyScaffolder output 'Views\MyMailer\Hello.cshtml'
Added MyScaffolder output 'Views\MyMailer\_Layout.text.cshtml'
Added MyScaffolder output 'Views\MyMailer\Hello.text.cshtml'

Send Email Asynchronously

You can simply use the SendAsync extension method for MailMessage:
using Mvc.Mailer;
...

public ActionResult SendWelcomeMessage()
{
    UserMailer.Welcome().SendAsync();
    return RedirectToAction("Index");
}

Test Asynchronous Email Sending

If you use SendAsyc, you can write the following code to test it:
[TearDown]
public void TearDown()
{
    TestSmtpClient.SentMails.Clear();
    TestSmtpClient.WasLastCallAsync = false;
}

[Test]
public void Test_SendWelcomeMessage_sends_async()
{
    //Arrange
    var mailMessage = new MailMessage();
    _userMailerMock.Setup(userMailer => userMailer.Welcome()).Returns(mailMessage);

    //Act
    var actionResult = _homeController.SendWelcomeMessage();

    //Assert
    _userMailerMock.VerifyAll();
    Assert.IsTrue(TestSmtpClient.WasLastCallAsync);
}

Handle Events for Asynchronous Email

You may want to handle events related to Asynchronous Emails. For example, you might need to take action when an asynchronous email sending is successful or not. Here's an example for you:
var client = new SmtpClientWrapper();

client.SendCompleted += (sender, e) =>
{
    if (e.Error != null || e.Cancelled)
    {
        // Handle Error
    }

    //Use e.UserState
};

new MyMailer().Welcome().SendAsync("user state object", client);

Embed Image or LinkedResource Inside Email

Sometimes you want to embed an image or other resources directly inline with the email. This is better for cases when you want the recipients to see the images and other resources while offline. MvcMailer makes is simpler for you. Here is an example:

In Your View

@Html.InlineImage("logo", "Company Logo")
Here, cid:logo will refer to the resource with Id logo. To set this resource, in your mailer do the following:

In Your Mailer

var resources = new Dictionary<string, string>();
resources["logo"] = logoPath;
PopulateBody(mailMessage, "WelcomeMessage", resources);

Email Sending from a Background Process

Do you need to send emails from a background process? Yes, you're right. You don't want to block your request/response cycle for that notification email to be sent. Instead, what you want is a background process that does it for you, even if it's sent after a short delay. Here's what you can do:
  • Save your email related data into a database.
  • Create a REST/SOAP web service that sends out the emails. This will ensure your Mailer has access to the HttpContext, which is essential for the core ASP.NET MVC framework to work properly. For example, to find your views, produce URLs, and perform authentication/authorization.
  • Create a simple App that calls the web service. This could be a windows service app or an executable app running under Windows Scheduled task.
A future version of MvcMailer is likely to have support for this. But it is hard because of two reasons:
  • MailMessage is not Serializable out of the box and has a lot of complex fields and associations.
  • The core ASP.NET framework still needs HttpContext :(

Upcoming Features

  • Email sending from a background process
  • VB Code example

Troubleshooting