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.
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.
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.
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!