Building Dynamic Html Template Newsletter Application In Asp.Net MVC With Hangfire and Sendgrid



This tutorial teaches you how to create and send dynamic newsletter email using Sengrid API and Hangfire.

Sendgrid is a cloud-based email service to solve the challenges of reliably delivering emails. You can read more about them on their website.

Hangfire make it easy to perform background processing in .NET with no window service or separate process required. It is backed by persistent storage. It is open source.


What you will learn

After reading this article you will know how to perform background task in Asp.Net MVC application using Hangfire. You will also know how to create a dynamic html template driven email and send to subscribers.

What we will be building

We are going to be building a cart reminder like we have in an e-commerce application. There will be three customers each of which had added products to their cart for more than two days but never complete their order.

The email sent will contain those products, images, quantity and price. It will also contain an action button to checkout.

Since we now know what we are going to be building let start building it.

Sign up for Sendgrid

Sign up for a sendgrid account. Generate and copy you API key.

The ASP.Net MVC Project

Create an ASP.Net MVC web application and install Sendgrid API like so:


The Database Structure

The application database will be very simple. We will have three tables namely Product, Cart and Customer. The product table will hold all the products, the customer table holds all customer and the cart table holds all product that has been added to cart by each customer. The structure of the tables is shown below:



Install and Configure Hangfire

Right click the references node withing your visual studio and choose Manage NuGet Packages, and search for Hangfire. Make sure you install the one by Sergey Odinokov.

After installation, add a startup class to the root of your Web Application by right clicking on the project name and select add class, search for OWIN Startup class and then add it.

Modify the newly added Startup class like so:

using System;
using System.Threading.Tasks;
using Hangfire;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(ASPNetNewsLetterHangfire.Startup))]

namespace ASPNetNewsLetterHangfire
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            GlobalConfiguration.Configuration
                .UseSqlServerStorage("HangFireConnectionString");

            app.UseHangfireDashboard();
            app.UseHangfireServer();
        }
    }
}



As I mentioned before, Hangfire uses a persistent storage to track queue jobs, in our own case we have specified SQL Server by calling the UseSqlServerStorage method and passing the connectionstring of our database. Hangfire will create its own tables.

Seeding the Database

I have added three customers and three product in their cart. The database with the data can be found in the source code download.

Sendgrid Email Service Class

Create a folder named helpers within your web application and add a class named EmailHelper like so:

    public class EmailHelper
    {
        string apiKey = ConfigurationManager.AppSettings["SendGripAPIKey"];
        string fromEmail = ConfigurationManager.AppSettings["FromEmail"];
        string fromEmailDisplayName = ConfigurationManager.AppSettings["FromEmailDisplayName"];

        public async Task Send(string subject, string to, string body, string cc = null, string bcc = null)
        {

            var mailClient = new SendGridClient(apiKey);
            var message = new SendGridMessage()
            {
                From = new EmailAddress(fromEmail, fromEmailDisplayName),
                Subject = subject,
                HtmlContent = body
            };

            message.AddTo(new EmailAddress(to));

            if (!string.IsNullOrWhiteSpace(cc)) { message.AddBcc(new EmailAddress(cc)); }

            if (!string.IsNullOrWhiteSpace(bcc)) { message.AddBcc(new EmailAddress(bcc)); }

            var response = await mailClient.SendEmailAsync(message);
            return response.StatusCode.ToString();

        }
    }


In the code above we got apikey, fromEmail and fromDisplay name from our ApSetting in the web.config.

The Email HTML Template
I added two html files in the App_Data folder. The first one is named ProductListTemplate.html and the second one is named MailTemplate.html.

You can find the template in the source code.
The next thing is to write the method that will be used to build the template. Create a class within the Helpers folder named TemplateHelper and add the following codes:
 public class TemplateHelper
    {
        public static string BuildProductListTemplate(string productName, decimal productPrice, string productImage)
        {


            var body = string.Empty;
            using (StreamReader reader = new StreamReader(Path.Combine(HttpRuntime.AppDomainAppPath,"~/App_Data/ProductListTemplate.html")))
            {
                body = reader.ReadToEnd();
            }

            body = body.Replace("{productImage}", productImage)
                .Replace("{productName}", productName)
                .Replace("{productPrice}", productPrice.ToString("#,##.00"));

            return body;
        }


        public static string BuildEmailTemplate(string productList, string customerName)
        {

            var body = string.Empty;
            using (StreamReader reader = new StreamReader(Path.Combine(HttpRuntime.AppDomainAppPath, "~/App_Data/MailTemplate.html")))
            {
                body = reader.ReadToEnd();
            }

            body = body.Replace("{customerName}", customerName)
                .Replace("{productList}", productList);

            return body;
        }
    }

As you can see, I read the template file into stream and then replace those tokens with the actual value from database.


Read Customer Data And Send Mail

Create a new Controller named NewsController and add the following code:

public async Task SendMails()
        {
            var newsEntities = new NewletterEntities();
            var customers = newsEntities.Customers;
            foreach (var item in customers)
            {
                var customerproducts = (from p in newsEntities.Products
                                        join crt in newsEntities.Carts
                                        on p.ProductId equals crt.ProductId
                                        join cust in newsEntities.Customers
                                        on crt.CartId equals cust.CustomerId.ToString()
                                        where cust.CustomerId == item.CustomerId
                                        select new
                                        {
                                            productName = p.ProductName,
                                            productImage = p.ProductImage,
                                            productPrice = p.Price
                                        }).ToList();

                StringBuilder orders = new StringBuilder();
                if (customerproducts.Any())
                {
                    foreach (var prod in customerproducts)
                    {
                        var productTemplate = TemplateHelper.BuildProductListTemplate(prod.productName, prod.productPrice, prod.productImage);

                        orders.Append(productTemplate);
                    }

                    var emailBody = TemplateHelper.BuildEmailTemplate(orders.ToString(), item.CustomerName);

                    var mailService = new EmailHelper();
                    var mailResponse = await mailService.Send("Order Reminder", item.Email, emailBody);
                }
            }
            
        }

Now open the Starup.cs class and add this code:


 app.UseHangfireDashboard("/jobs");
            var ctrl = new NewsController();
            RecurringJob.AddOrUpdate(() => ctrl.SendMails(), Cron.MinuteInterval(10));
Now the full Startup.cs file should look like this:
 public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            GlobalConfiguration.Configuration
                .UseSqlServerStorage("HangFireConnectionString")
                .UseDashboardMetric(DashboardMetrics.FailedCount);

            app.UseHangfireDashboard("/jobs");
            var ctrl = new NewsController();
            RecurringJob.AddOrUpdate(() => ctrl.SendMails(), Cron.MinuteInterval(10)); //Send Every 10 minutes          
            app.UseHangfireServer();
        }
    }
I made a reference to the NewsController and then add Hangfire recurring job and set it to fire every 10 minutes. You can read more in hangfire website for more configurations. The app.UseHangfireDashboard("/jobs") will allow us view hangfire recurring jobs via http://yourwebapp:port/jobs.

I hope this helps. Thanks for stopping by.

No comments

Powered by Blogger.