Try our conversational search powered by Generative AI!

cecilia@nansen.se
Jul 1, 2011
  8660
(4 votes)

Implementing a simple email queue in EPiServer Commerce

Recently I’ve been working with a Commerce project where when a customer has placed an order we wanted to send a confirmation email. In the EPiServer Commerce sample site the code to send the confirmation email is in the code behind of the cart checkout control. This isn’t a good practice as if something goes wrong with the email sending, for example, if the SMTP configuration is incorrect, then the user will see an error saying something went wrong with their order, which in this case is not true.

What we decided to do was to queue up an email request using the EPiServer Dynamic Data Store and then use an EPiServer Scheduled Job to service the email queue and send out the emails.

First of all we created a simple class with the information needed to send the email:

using System;
using EPiServer.Data.Dynamic;

namespace Project.Foundation.CMS.Helpers
{   
    [EPiServerDataStore(AutomaticallyCreateStore = true)]
    public class OrderEmailToBeSent
    {
        public int OrderId { get; set; }
        public Guid Id { get; set; }
    }
}

Then when an order is placed we created an instance of the above class, set it’s properties and saved it in the DDS:

private void SaveOrderId(int orderId)
{
     var store = typeof(OrderEmailToBeSent).GetStore();
     var orderEmailToBeSent = new OrderEmailToBeSent { OrderId = orderId };
     store.Save(orderEmailToBeSent);
}

The Scheduled Job class gets the items from the Dynamic Data Store, loads the order from the Commerce system, sends an email to the customer, and then deletes the item from the DDS:

[ScheduledPlugIn(DisplayName = "Send order emails")]
 class SendOrderEmails
 {
     public static string Execute()
     {
         var store = typeof(OrderEmailToBeSent).GetStore();
         int skip = 0;
         const int count = 50;
         while (true)
         {
             var items = store.Items<OrderEmailToBeSent>().Skip(skip).Take(count);

             if (items.Count() == 0)
                 break;

             foreach (var orderEmailToBeSent in items)
             {
                 var order = CartHelper.FindOrder(orderEmailToBeSent.OrderId);

                 SendEmail(order, "order-purchaseorder-notify");
                 store.Delete(orderEmailToBeSent);
             }

             skip += count;
      }
         return "Success!";
     }
 }

And the code for sending the email:

private static void SendEmail(PurchaseOrder order, string template)
{
      // Add input parameter
      var dic = new Dictionary<string, object>();
      dic.Add("OrderGroup", order);

      // Execute template processor
      var body = TemplateService.Process(template,                  
Thread.CurrentThread.CurrentCulture, dic); // Send out emails var msg = new MailMessage(); msg.From = new MailAddress("store@yourcompany.com"); msg.To.Add(new MailAddress("customer@customer.com", " - " + order.Name)); msg.Subject = "Subject"; msg.Body = body; msg.IsBodyHtml = true; var client = new SmtpClient(); client.Send(msg); }

The TemplateService class used above takes an XSL template and transforms it into an HTML email. See this article for more details about this.

Jul 01, 2011

Comments

Jul 1, 2011 12:28 AM

Awesome!!

Jul 1, 2011 05:59 AM

Very nice!!!

Leif Boström
Leif Boström Jul 1, 2011 08:35 AM

Great post!

Jul 1, 2011 08:46 AM

Super stuff! :-)

Jul 1, 2011 08:05 PM

Good stuff. :)

Jul 1, 2011 08:05 PM

Good stuff. :)

Jul 1, 2011 10:04 PM

Clean and simple, I like it!

cecilia@nansen.se
cecilia@nansen.se Jul 2, 2011 02:25 AM

Thanks! Also thanks to Paul Smith for the idea.

Jul 4, 2011 01:26 PM

Clever thinking. I guess this is a one language solution. In a multilang solution you probably want the email in the users locale, and should store the culture code in the queue too. The scheduler will run with the default locale (web.config)

Jul 4, 2011 05:09 PM

Nice. One thing to think about is that the LINQ expression will be executed twice here. The "Count" will create and execute one LINQ expression against the database, and the foreach will create and execute another LINQ expression against the database.

In this case, it's maby what you want, but I just wanted to post a warning about it.

cecilia@nansen.se
cecilia@nansen.se Jul 4, 2011 05:34 PM

BQ, good point. I should set a flag in the foreach to indicate that there are items instead or use ToList() to convert the LINQ-query to a List.

Please login to comment.
Latest blogs
Change the IP HTTP Header used for geo-lookup in Application Insights

.

Johan Kronberg | Jun 10, 2024 | Syndicated blog

Copying property values

In this article I’d like to show simple Edit Mode extension for copying property values to other language versions. In one of my previous blogposts...

Grzegorz Wiecheć | Jun 8, 2024 | Syndicated blog

Auto-translate with OpenAI GPT-4o in Optimizely CMS

Improvements for Episerver.Labs.LanguageManager! It's now possible to auto-translate both a page and its children at the same time! Additionally, m...

Tomas Hensrud Gulla | Jun 7, 2024 | Syndicated blog

Upgrade To Optimizely CMS 12 Issue: List item fields have become Required

There are many funny details to be aware of when upgrading from Episerver CMS 11 to Optimizely CMS 12. One of them that might feel a bit confusing ...

Allan Thraen | Jun 7, 2024 | Syndicated blog

Integrating Apple Pay within Optimizely - Lesson's Learned

Hello Optimizely world, I have recently undertaken the responsibility of assisting our client in the integration of Apple Pay onto their website...

Silvio Pacitto | Jun 6, 2024

A day in the life of an Optimizely Developer - Introduction to Optimizely SaaS Core

What is SaaS Core Optimizely SaaS Core is a cloud-based, headless, and API-first version of the Optimizely CMS (Content Management System) and...

Graham Carr | Jun 4, 2024