You are here:   Blog
Register   |  Login

LightSwitch News

Sep 23

Written by: Michael Washington
9/23/2013 9:16 PM  RssIcon

image

This article demonstrates how you can send server-side asynchronous emails using the Visual Studio LightSwitch HTML Client. Sending emails using LightSwitch has been covered before, notably by Paul Patterson (some of his code is used in this article), the difference with this article is that the emails will be sent asynchronously, so they don’t block the user interface or cause it to hang while the email is being sent.

 

The Application

image

The first step is to open the Web.config file and set the values to connect to your SMTP email server.

image

Next, run the application and go to Settings and set the SendEmails value to true and click save.

If it is set to anything but true it will not try to send emails.

image

Go to the Watches screen and add a Watch.

image

Enter a Watch and click save.

image

Go to the Listings screen and add a Listing.

image

Enter a Listing and click save.

image

When we look in browse messages we see the email messages that were sent.

image

Clicking on a message allows us to see the details.

image

The Message Logs screen indicates if any of the messages encountered any errors when sending.

The Code

image

The first step is to create Watches.

A Watch record represents something users are waiting for.

If a Listing matches their Watch they want to receive an email.

image

A Listing record starts the process.

When a Listing record is inserted the following code runs:

 

       partial void Listings_Inserting(Listing entity)
        {
            // Elevate the user's permission to allow the Messages Entity to be updated
            this.Application.User.AddPermissions(Permissions.SecurityAdministration);
            // Send the Email to user creating the Listing
            Message objMessage = new Message();
            objMessage.NameFrom = Convert.ToString(ConfigurationManager.AppSettings["SMTPSendingName"]);
            objMessage.EmailFrom = Convert.ToString(ConfigurationManager.AppSettings["SMTPSendingEmailAddress"]);
            objMessage.NameTo = entity.UserNameInfo.FullName;
            objMessage.EmailTo = entity.EmailAddress;
            objMessage.EmailMessage = string.Format("Your listing for {0} was created.", entity.Product);
            // Search for all matching Watches
            char charSeparator = Convert.ToChar(" ");
            var ProductFilter = entity.Product.ToLower().Split(charSeparator).ToArray();
            var MatchingWatches = from objWatch in this.DataWorkspace.ApplicationData.Watches
                                  where objWatch.PriceHigh >= entity.Price
                                  where objWatch.PriceLow <= entity.Price
                                  where objWatch.Enabled == true
                                  where (
                                  objWatch.SearchString == null
                                  || objWatch.SearchString == ""
                                  || ProductFilter.Any(x => x.Contains(objWatch.SearchString.ToLower())))
                                  select objWatch;
            // Send an email to each matching Watch
            foreach (Watch item in MatchingWatches)
            {
                Message objWatchMessage = new Message();
                objWatchMessage.NameFrom = Convert.ToString(
                    ConfigurationManager.AppSettings["SMTPSendingName"]);
                objWatchMessage.EmailFrom = Convert.ToString(
                    ConfigurationManager.AppSettings["SMTPSendingEmailAddress"]);
                objWatchMessage.NameTo = item.UserNameInfo.FullName;
                objWatchMessage.EmailTo = item.EmailAddress;
                objWatchMessage.EmailMessage = string.Format(
                    "A Product named {0} matching your watch has been posted.", entity.Product);
            }
        }

 

The code above just inserts a record into the Message table, it does not actually send the email.

We do this because we also want a record of each email sent. This diagnostic information is important and helpful in a real enterprise application.

Note: The Messages table, like most tables in the application have code such as the following to prevent non-administrators from being able to read the data:

 

        partial void Messages_CanDelete(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.SecurityAdministration);
        }
        partial void Messages_CanInsert(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.SecurityAdministration);
        }
        partial void Messages_CanRead(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.SecurityAdministration);
        }
        partial void Messages_CanUpdate(ref bool result)
        {
            result = this.Application.User.HasPermission(Permissions.SecurityAdministration);
        }

 

That is why we have to use code such as this when we want to write to the table:

 

            // Elevate the user's permission to allow the Messages Entity to be updated
            this.Application.User.AddPermissions(Permissions.SecurityAdministration);

 

image

The Message table runs the following code when a record is inserted that actually sends the email:

 

     partial void Messages_Inserted(Message entity)
        {
            try
            {
                // Get Email Settings
                var EmailSetting = Settings.Where(x => x.SettingName == "SendEmails").FirstOrDefault();
                if (EmailSetting != null)
                {
                    if (EmailSetting.SettingValue.ToLower() == "true")
                    {
                        string strSubject = "Message From LightSwitch Email Server";
                        string strMessage = String.Format("{0}",
                            "This is a message From the LightSwitch Email Server")
                            + Environment.NewLine + Environment.NewLine;
                        strMessage = strMessage + String.Format("{0}",
                            entity.EmailMessage) + Environment.NewLine;
                        // Create the MailHelper class created in the Server project.
                        MailHelper mailHelper =
                            new MailHelper(
                                entity.NameFrom,
                                entity.EmailTo,
                                entity.NameTo,
                                strSubject,
                                strMessage);
                        // Set up a handler to fire when email has been sent
                        mailHelper.objSmtpClient.SendCompleted +=
                            new System.Net.Mail.SendCompletedEventHandler(MailDeliveryComplete);
                        // Send Email
                        mailHelper.SendMail();
                    }
                    else
                    {
                        // Emails are turned off
                        LogToMessageLogTable(
                            "No emails were sent because SendEmails is set to False"
                            );
                    }
                }
                else
                {
                    // No Email Settings
                    LogToMessageLogTable(
                        "No emails were sent because there is no record for SendEmails in Email Settings"
                        );
                }
            }
            catch (Exception ex)
            {
                // Log
                LogToMessageLogTable(ex.Message);
            }
        }

 

Next, we want to log the MessageLog table when the email has been sent.

We wire up a method to the SendCompletedEventHandler (in the code above) to call our MailDeliveryComplete method (below):

 

        private void MailDeliveryComplete(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
        {
            string LogMessage = "";
            if (e.Error != null)
            {
                LogMessage = string.Format("{0} - {1}", e.UserState, e.Error);
            }
            else if (e.Cancelled)
            {
                LogMessage = string.Format("{0} - {1}", e.UserState, "Cancelled");
            }
            else
            {
                LogMessage = string.Format("{0} - {1}", e.UserState, "Message Sent");
            }
            // Log
            LogToMessageLogTable(LogMessage);
        }

 

When the code above runs, it is not running in the normal user context, so the normal LightSwitch server side data access code wont work.

We use an old fashioned ADO.NET code to write to the Messagelog table:

 

        private static void LogToMessageLogTable(string LogMessage)
        {
            // Insert into the database
            // Do this using ADO.NET because we don't have an active 
            // LightSwitch Dataworkspace at this point
            string connString =
                System.Web.Configuration.WebConfigurationManager
                .ConnectionStrings["_IntrinsicData"].ConnectionString;
            SqlConnection conn = new SqlConnection(connString);
            string sql = "INSERT INTO MessageLogs (LogMessage, Created) VALUES (@LogMessage, @Created)";
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            cmd.Parameters.AddWithValue("@LogMessage", LogMessage);
            cmd.Parameters.AddWithValue("@Created", DateTime.UtcNow);
            cmd.CommandType = CommandType.Text;
            cmd.ExecuteNonQuery();
            conn.Close();
        }

 

The following class is used to actually send the emails:

 

// Adapted from:
// http://www.paulspatterson.com/technology/lightswitch/
//   microsoft-lightswitch-sending-emails-from-the-client/#more-2896
using System.Net;
using System.Net.Mail;
using System.Configuration;
using System;
namespace LightSwitchApplication
{
    public class MailHelper
    {
        public SmtpClient objSmtpClient { get; set; }
        private string _SMTPSendingEmailAddress { get; set; }
        private string _SMTPServer { get; set; }
        private string _SMTPUserId { get; set; }
        private string _SMTPPassword { get; set; }
        private int _SMTPPort { get; set; }
        private bool _SMTPSSL { get; set; }
        private string _MailFromName { get; set; }
        private string _MailToEmail { get; set; }
        private string _MailToName { get; set; }
        private string _MailSubject { get; set; }
        private string _MailBody { get; set; }
        public MailHelper(
            string SendFromName, string SendToEmail,
            string SendToName, string Subject,
            string Body)
        {
            _MailFromName = SendFromName;
            _MailToEmail = SendToEmail;
            _MailToName = SendToName;
            _MailSubject = Subject;
            _MailBody = Body;
            _SMTPSendingEmailAddress = Convert.ToString(ConfigurationManager.AppSettings["SMTPSendingEmailAddress"]);
            _SMTPServer = Convert.ToString(ConfigurationManager.AppSettings["SMTPServer"]);
            _SMTPUserId = Convert.ToString(ConfigurationManager.AppSettings["SMTPUserID"]);
            _SMTPPassword = Convert.ToString(ConfigurationManager.AppSettings["SMTPPassword"]);
            _SMTPPort = Convert.ToInt32(ConfigurationManager.AppSettings["SMTPPort"]);
            _SMTPSSL = Convert.ToBoolean(ConfigurationManager.AppSettings["SMTPSSL"]);
            objSmtpClient = new SmtpClient(_SMTPServer, _SMTPPort);
        }
        public void SendMail()
        {
            MailMessage mail = new MailMessage();
            System.Net.Mail.MailAddress mailFrom =
                new System.Net.Mail.MailAddress(_SMTPSendingEmailAddress, _MailFromName);
            System.Net.Mail.MailAddress mailTo =
                new System.Net.Mail.MailAddress(_MailToEmail, _MailToName);
            var _with1 = mail;
            _with1.From = mailFrom;
            _with1.To.Add(mailTo);
            _with1.Subject = _MailSubject;
            _with1.Body = _MailBody;
            
            objSmtpClient.EnableSsl = _SMTPSSL;
            objSmtpClient.Credentials =
                new NetworkCredential(_SMTPUserId, _SMTPPassword);
            objSmtpClient.SendAsync(mail, mail.To);
        }
    }
}

 

Database Project

image

To automatically create the record for the Setting table (that indicates if emails should be sent or not) we add a SQL Server Database Project.

image

We then add a Post-Deployment Script to the SQL Server Database Project and use the following code:

 

/*
Post-Deployment Script Template							
--------------------------------------------------------------------------------------
 This file contains SQL statements that will be appended to the build script.		
 Use SQLCMD syntax to include a file in the post-deployment script.			
 Example:      :r .\myfile.sql								
 Use SQLCMD syntax to reference a variable in the post-deployment script.		
 Example:      :setvar TableName MyTable							
               SELECT * FROM [$(TableName)]					
--------------------------------------------------------------------------------------
*/
SET IDENTITY_INSERT Settings ON;
 
MERGE INTO Settings AS Target
USING (VALUES
  (1, 'SendEmails','Off' )
) 
AS Source(Id, SettingName, SettingValue) 
ON Target.Id = Source.Id 
-- update matched rows 
WHEN MATCHED THEN
UPDATE SET SettingName = Source.SettingName, SettingValue = Source.SettingValue
-- insert new rows 
WHEN NOT MATCHED BY TARGET THEN
INSERT (Id, SettingName, SettingValue)
VALUES (Id, SettingName, SettingValue)
-- delete rows that are in the target but not the source 
WHEN NOT MATCHED BY SOURCE THEN
DELETE;
 
SET IDENTITY_INSERT Settings OFF;
 
GO

 

Delete All Button

image

On most screens there is a Delete All button to allow you to quickly clear away test data. 

image

When you click the button, a confirmation box shows before it deletes any records.

This is the code that is used:

 

myapp.BrowseMessages.DeleteAll_execute = function (screen) {
    msls.showMessageBox("All content will be deleted.", {
        title: "Confim Delete",
        buttons: msls.MessageBoxButtons.okCancel
    })
    .then(function (result) {
        if (result === msls.MessageBoxResult.ok) {
            // Get all Messages
            myapp.activeDataWorkspace.ApplicationData.Messages
                .load()
                .then(function (result) {
                    $.each(result.results, function (index, message) {
                        // Delete the Message
                        message.deleteEntity();
                    });
                    // Save all changes
                    return myapp.commitChanges().then(null, function fail(e) {
                        myapp.cancelChanges();
                        alert(e);
                        throw e;
                    });
                });
        };
    });
};

 

 

Download Code

The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

(you must have Visual Studio 2013 (or higher) installed to run the code)

Tags: VS2013
Categories:

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation