How To Send HTML Email from a LightSwitch Application

A while back Paul Patterson wrote an awesome step-by-step blog post based on a forum question on how to send an automated email using System.Net.Mail from LightSwitch. If you missed it here it is:

Microsoft LightSwitch – Send an Email from LightSwitch

This is a great solution if you want to send email in response to some event happening in the data update pipeline on the server side. In his example he shows how to send a simple text-based email using an SMTP server in response to a new appointment being inserted into the database. In this post I want to show you how you can create richer HTML mails from data and send them via SMTP. I also want to present a client-side solution that creates an email using the Outlook client which allows the user to see and modify the email before it is sent.

Sending Email via SMTP

As Paul explained, all you need to do to send an SMTP email from the LightSwitch middle-tier (server side) is switch to File View on the Solution Explorer and add a class to the Server project. Ideally you’ll want to put the class in the UserCode folder to keep things organized.

image

TIP: If you don’t see a UserCode folder that means you haven’t written any server rules yet. To generate this folder just go back and select any entity in the designer, drop down the “Write Code” button on the top right, and select one of the server methods like entity_Inserted.

The basic code to send an email is simple. You just need to specify the SMTP server, user id, password and port. TIP: If you only know the user ID and password then you can try using Outlook 2010 to get the rest of the info for you automatically.

Notice in my SMTPMailHelper class I’m doing a quick check to see whether the body parameter contains HTML and if so, I set the appropriate mail property.

 Imports System.Net
Imports System.Net.Mail

Public Class SMTPMailHelper
    Const SMTPServer As String = "smtp.mydomain.com"
    Const SMTPUserId As String = "myemail@mydomain.com"
    Const SMTPPassword As String = "mypassword"
    Const SMTPPort As Integer = 25
     Public Shared Sub SendMail(ByVal sendFrom As String,
                               ByVal sendTo As String,
                               ByVal subject As String,
                               ByVal body As String)
         Dim fromAddress = New MailAddress(sendFrom)
        Dim toAddress = New MailAddress(sendTo)
        Dim mail As New MailMessage

        With mail
            .From = fromAddress
            .To.Add(toAddress)
            .Subject = subject

            If body.ToLower.Contains("<html>") Then
                .IsBodyHtml = True
            End If

            .Body = body
        End With

        Dim smtp As New SmtpClient(SMTPServer, SMTPPort)
        smtp.Credentials = New NetworkCredential(SMTPUserId, SMTPPassword)
        smtp.Send(mail)
    End Sub
End Class

Creating HTML from Entity Data

Now that we have the code to send an email I want to show you how we can quickly generate HTML from entity data using Visual Basic’s XML literals. (I love XML literals and have written about them many times before.) If you are new to XML literals I suggest starting with this article and this video. To use XML literals you need to make sure you have an assembly reference to System.Core, System.Xml, and System.Xml.Linq.

What I want to do is create an HTML email invoice for an Order entity that has children Order_Details. First I’ve made my life simpler by adding computed properties onto the Order_Details and Order entities that calculate line item and order totals respectively. The code for these computed properties is as follows:

 Public Class Order_Detail
    Private Sub LineTotal_Compute(ByRef result As Decimal)
        ' Calculate the line item total for each Order_Detail
        result = (Me.Quantity * Me.UnitPrice) * (1 - Me.Discount)
    End Sub
End Class

 Public Class Order
    Private Sub OrderTotal_Compute(ByRef result As Decimal)
        ' Add up all the LineTotals on the Order_Details collection for this Order
        result = Aggregate d In Me.Order_Details Into Sum(d.LineTotal)
    End Sub
End Class

Next I want to send an automated email when the Order is inserted into the database. Open the Order entity in the designer and then drop down the “Write Code” button on the top right and select Order_Inserted to generate the method stub. To generate HTML all you need to do is type well formed XHTML into the editor and use embedded expressions to pull the data out of the entities.

 Public Class NorthwindDataService
    Private Sub Orders_Inserted(ByVal entity As Order)
        Dim toEmail = entity.Customer.Email

        If toEmail <> "" Then
            Dim fromEmail = entity.Employee.Email
            Dim subject = "Thank you for your order!"

            Dim body = <html>
                           <body style="font-family: Arial, Helvetica, sans-serif;">
                               <p><%= entity.Customer.ContactName %>, thank you for your order!<br></br>
                               Order date: <%= FormatDateTime(entity.OrderDate, DateFormat.LongDate) %></p>
                               <table border="1" cellpadding="3"
                                   style="font-family: Arial, Helvetica, sans-serif;">
                                   <tr>
                                       <td><b>Product</b></td>
                                       <td><b>Quantity</b></td>
                                       <td><b>Price</b></td>
                                       <td><b>Discount</b></td>
                                       <td><b>Line Total</b></td>
                                   </tr>
                                   <%= From d In entity.Order_Details
                                       Select <tr>
                                          <td><%= d.Product.ProductName %></td>
                                          <td align="right"><%= d.Quantity %></td>
                                          <td align="right"><%= FormatCurrency(d.UnitPrice, 2) %></td>
                                          <td align="right"><%= FormatPercent(d.Discount, 0) %></td>
                                          <td align="right"><%= FormatCurrency(d.LineTotal, 2) %></td>
                                      </tr>
                                   %>
                                   <tr>
                                       <td></td>
                                       <td></td>
                                       <td></td>
                                       <td align="right"><b>Total:</b></td>
                                       <td align="right"><b><%= FormatCurrency(entity.OrderTotal, 2) %></b></td>
                                   </tr>
                               </table>
                           </body>
                       </html>

            SMTPMailHelper.SendMail(fromEmail, toEmail, subject, body.ToString)
        End If
    End Sub
End Class

The trick is to make sure your HTML looks like XML (i.e. well formed begin/end tags) and then you can use embedded expressions (the <%= syntax) to embed Visual Basic code into the HTML. I’m using LINQ to query the order details to populate the rows of the HTML table. (BTW, you can also query HTML with a couple tricks as I show here).

So now when a new order is entered into the system an auto-generated HTML email is sent to the customer with the order details.

image

Sending Email via an Outlook Client

 The above solution works well for sending automated emails but what if you want to allow the user to modify the email before it is sent? In this case we need a solution that can be called from the LightSwitch UI. One option is to automate Microsoft Outlook -- most people seem to use that popular email client, especially my company ;-). Out of the box, LightSwitch has a really nice feature on data grids that lets you to export them to Excel if running in full trust. We can add a similar Office productivity feature to our screen that auto generates an email for the user using Outlook. This will allow them to modify it before it is sent.

We need a helper class on the client this time. Just like in the SMTP example above, add a new class via the Solution Explorer file view but this time select the Client project. This class uses COM automation, a feature of Silverlight 4 and higher. First we need to check if we’re running out-of-browser on a Windows machine by checking the AutomationFactory.IsAvailable property. Next we need to get a reference to Outlook, opening the application if it’s not already open. The rest of the code just creates the email and displays it to the user.

 Imports System.Runtime.InteropServices.Automation

Public Class OutlookMailHelper
    Const olMailItem As Integer = 0
    Const olFormatPlain As Integer = 1
    Const olFormatHTML As Integer = 2

    Public Shared Sub CreateOutlookEmail(ByVal toAddress As String,
                                         ByVal subject As String,
                                         ByVal body As String)
        Try
            Dim outlook As Object = Nothing

            If AutomationFactory.IsAvailable Then
                Try
                    'Get the reference to the open Outlook App
                    outlook = AutomationFactory.GetObject("Outlook.Application")

                Catch ex As Exception 'If Outlook isn't open, then an error will be thrown.
                    ' Try to open the application
                    outlook = AutomationFactory.CreateObject("Outlook.Application")
                End Try

                If outlook IsNot Nothing Then
                    'Create the email

                    ' Outlook object model (OM) reference: 
                    ' https://msdn.microsoft.com/en-us/library/ff870566.aspx

                    Dim mail = outlook.CreateItem(olMailItem)
                    With mail
                        If body.ToLower.Contains("<html>") Then
                            .BodyFormat = olFormatHTML
                            .HTMLBody = body
                        Else
                            .BodyFormat = olFormatPlain
                            .Body = body
                        End If

                        .Recipients.Add(toAddress)
                        .Subject = subject

                        .Save()
                        .Display()
                        '.Send()
                    End With
                End If
            End If

        Catch ex As Exception
            Throw New InvalidOperationException("Failed to create email.", ex)
        End Try
    End Sub
End Class

The code to call this is almost identical to the previous example. We use XML literals to create the HTML the same way. The only difference is we want to call this from a command button on our OrderDetail screen. (Here’s how you add a command button to a screen.) In the Execute method for the command button is where we add the code to generate the HTML email. I also want to have the button disabled if AutomationFactory.IsAvailable is False and you check that in the CanExecute method.

Here’s the code we need in the screen:

 Private Sub CreateEmail_CanExecute(ByRef result As Boolean)
    result = System.Runtime.InteropServices.Automation.AutomationFactory.IsAvailable
End Sub

Private Sub CreateEmail_Execute()
    'Create the html email from the Order data on this screen
    Dim toAddress = Me.Order.Customer.Email
    If toAddress <> "" Then

        Dim entity = Me.Order
        Dim subject = "Thank you for your order!"

        Dim body = <html>
                       <body style="font-family: Arial, Helvetica, sans-serif;">
                           <p><%= entity.Customer.ContactName %>, thank you for your order!<br></br>
                          Order date: <%= FormatDateTime(entity.OrderDate, DateFormat.LongDate) %></p>
                           <table border="1" cellpadding="3"
                               style="font-family: Arial, Helvetica, sans-serif;">
                               <tr>
                                   <td><b>Product</b></td>
                                   <td><b>Quantity</b></td>
                                   <td><b>Price</b></td>
                                   <td><b>Discount</b></td>
                                   <td><b>Line Total</b></td>
                               </tr>
                               <%= From d In entity.Order_Details
                                   Select <tr>
                                      <td><%= d.Product.ProductName %></td>
                                      <td align="right"><%= d.Quantity %></td>
                                      <td align="right"><%= FormatCurrency(d.UnitPrice, 2) %></td>
                                      <td align="right"><%= FormatPercent(d.Discount, 0) %></td>
                                      <td align="right"><%= FormatCurrency(d.LineTotal, 2) %></td>
                                  </tr>
                               %>
                               <tr>
                                   <td></td>
                                   <td></td>
                                   <td></td>
                                   <td align="right"><b>Total:</b></td>
                                   <td align="right"><b><%= FormatCurrency(entity.OrderTotal, 2) %></b></td>
                               </tr>
                           </table>
                       </body>
                    </html>

        OutlookMailHelper.CreateOutlookEmail(toAddress, subject, body.ToString)
    Else
        Me.ShowMessageBox("This customer does not have an email address",
                          "Missing Email Address",
                          MessageBoxOption.Ok)
    End If
End Sub

Now when the user clicks the Create Email button on the ribbon, the HTML email is created and the Outlook mail message window opens allowing the user to make changes before they hit send.

image

I hope I’ve provided a couple options for sending HTML emails in your LightSwitch applications. Select the first option to use SMTP when you want automated emails sent from the server side. Select the second option to use the Outlook client when you want to interact with users that have Outlook installed and LightSwitch is running out-of-browser.

Enjoy!