Wednesday, September 9, 2015

Sending Automated Emails asynchronously using a C# Windows Service in conjunction with Database Email records

Hi...
1.     This part of the tutorial is especially focused on the Windows Service creation, configuration and installation. The concepts (precisely the database and class library) used in this part is already created and explained in the Part 1 of the tutorial.
2.     Apart from the continuation from Part –1, this Part can actually be implemented separately according to the requirement. At the same time, this part of the tutorial can be implemented with variety of concepts like XML, EF, LINQ etc. So it is typically based on the requirement to make different pieces fall in place.
In this part of the tutorial, we first create a Windows Service Project, then implement a Service called – ‘BirthdayEmailService’. Then we add a reference to our previously created Class Library – ‘EmailComponent’. We code BirthdayEmailService using Timer to perform the email sending task for every hour. Then we incorporate the Installer classes for the purpose of installation. Finally a Setup and Deployment Project is created for the Service. Finally the complete Solution is build and the exe file is generated which is ready enough to get installed.
IMPORTANT – The logic in the using the Timer is to send emails in particular intervals of time. The timer interval is set to 60mins, and it is set to that value solely for demonstration purpose. According to the written logic, emails will be send only for the first time elapsed event of a particular date, so that the repetition of sending emails to the same customers can be avoided. Always remember, logic in the service Time Event can be modified according to ones requirement.
================================================================================================
BirthdayEmailService Project
This section narrates how to create a Windows Project. It also discuss how to prepare the project according to our requirement (say adding references etc).
================================================================================================
Let’s get started with the creation of Windows Service.
1.     Open VS 2010
2.     File -> New -> Project
3.     Select ‘Visual C#’ -> Windows -> Windows Service Template -> Give Name ‘BirthdayEmailSevice’
4.     Delete the default Service1.cs
To access Database i.e., CustomerDB, we include a app.config file which in this case hold the data corresponding to the database connectivity ( like connection string etc).
To add app.config file -
1.     Right click ‘BirthdayEmailService’ in the solution explorer –> Select ‘Add New Item’.
2.     Select ‘Visual C#’ -> Select Application Configuration File Template -> Click ADD.
To make the ConfigurationManager ( which is used to access config file settings in the code), we add reference to System.Configuration.dll to the project (right click Project ‘BirthdayEmailService’ -> Select Add Reference -> from .Net tab, Select System.Configuaration -> click ADD).
Put the following code in the created app.config file –
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="CustomerDBConnectionString"
       connectionString="Data Source=RAMILU-PC\SQLEXPRESS;Initial Catalog=CustomerDB;Integrated Security=True;Pooling=False"
       providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>
Now let’s add a Service to the project –
1.     Right Click ‘BirthdayEmailService’ in the Solution Explorer –> Select ‘Add New Item’.
2.     Select ‘Visual C# Items’ -> Select Windows Service Template -> Give name – ‘BirthdayEmailService.cs’.
3.     Click ADD.
To Code against the EmailComponent (which we created earlier), we got to add reference of it to our service.
1.     Right Click ‘References’ of BirthdayEmailService –> Select ‘Add Reference’.
2.     Select ‘Browse’ tab -> Navigate to the ‘EmaiComponent’ project folder -> Select the EmailComponent.dll in the bin/Debug folder of ‘EmailComponent’ project. (NOTE: Before adding the reference, build the EmailComponent Project so that it would be up to date.)
3.     Click OK.
One last thing which is left behind is to change the Entry point of the application -
1.     Open Program.cs of the newly created service.
2.     Replace the Main() function with the following one –
        static void Main()
        {
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                                {
                                    new BirthdayEmailService()
                                };
                ServiceBase.Run(ServicesToRun);
        }
Now, Writing code to BirthdayEmailService.cs –
1.     Open BirthdayEmailService.cs in designer mode in VS 2010.
2.     Drag and drop an EventLog component and Timer component (Please do not add this Timer component, I am adding this component from C# – Update : 1/16/2012) from the ToolBox on to the designer.
3.     Change their names to eventLogEmail and scheduleTimer (check (2), please do not add Timer component, I am adding this from C# – Update : 1/16/2012).
4.     EventLog is used to log the activities of the service, and timer is used to make the service run at particular intervals of time.
5.     Right click anywhere in the designer surface -> Select -> View Code.
Write the following code –
§  Change the Settings/Properties as per your own settings. Especially the Network Credentials and Username.
§  In the code, we first get the email id’s (in DataSet) of all the customers who are celebrating their birthday on that particular day, then we iterate through the through the DataSet and call the SendEmailAsync() method of EmailComponent dll. SendEmailAsync() method then process asynchronously and sends back the result.
§  We set the From Email, To Email, subject, Message Body etc. properties in this routine and pass the same values to the EmailComponent dll for further processing.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Timers;
using System.Configuration;
using EmailComponent;
 
namespace BirthdayEmailService
{
    partial class BirthdayEmailService : ServiceBase
    {
        private Timer scheduleTimer = null;
        private DateTime lastRun;
        private bool flag;
 
        public BirthdayEmailService()
        {
            InitializeComponent();
            if (!System.Diagnostics.EventLog.SourceExists("EmailSource"))
            {
                System.Diagnostics.EventLog.CreateEventSource("EmailSource", "EmailLog");
            }
            eventLogEmail.Source = "EmailSource";
            eventLogEmail.Log = "EmailLog";
 
            scheduleTimer = new Timer();
            scheduleTimer.Interval = 1 * 5 * 60 * 1000;
            scheduleTimer.Elapsed += new ElapsedEventHandler(scheduleTimer_Elapsed);
 
        }
 
        protected override void OnStart(string[] args)
        {
            flag = true;
            lastRun = DateTime.Now;
            scheduleTimer.Start();
            eventLogEmail.WriteEntry("Started");
        }
 
        protected void scheduleTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (flag == true)
            {
                ServiceEmailMethod();
                lastRun = DateTime.Now;
                flag = false;
            }
            else if (flag == false)
            {
                if (lastRun.Date < DateTime.Now.Date)
                {
                    ServiceEmailMethod();
                }
            }
        }
 
        private void ServiceEmailMethod()
        {
            eventLogEmail.WriteEntry("In Sending Email Method");
 
            EmailComponent.GetEmailIdsFromDB getEmails = new EmailComponent.GetEmailIdsFromDB();
            getEmails.connectionString = ConfigurationManager.ConnectionStrings["CustomerDBConnectionString"].ConnectionString;
            getEmails.storedProcName = "GetBirthdayBuddiesEmails";
            System.Data.DataSet ds = getEmails.GetMailIds();
 
            EmailComponent.Email email = new EmailComponent.Email();
 
            email.fromEmail = "example@gmail.com";
            email.fromName = "example Name";
            email.subject = "Birthday Wishes";
            email.smtpServer = "smtp.gmail.com";
            email.smtpCredentials = new System.Net.NetworkCredential("example@gmail.com", "example password");
 
            foreach (System.Data.DataRow dr in ds.Tables[0].Rows)
            {
                email.messageBody = "<h4>Hello " + dr["CustomerName"].ToString() + "</h4><br/><h3>We Wish you a very Happy" +
                                    "Birthday  to You!!! Have a bash...</h3><br/><h4>Thank you.</h4>";
 
                bool result = email.SendEmailAsync(dr["CustomerEmail"].ToString(), dr["CustomerName"].ToString());
 
                if (result == true)
                {
                    eventLogEmail.WriteEntry("Message Sent SUCCESS to - " + dr["CustomerEmail"].ToString() +
                                             " - " + dr["CustomerName"].ToString());
                }
                else
                {
                    eventLogEmail.WriteEntry("Message Sent FAILED to - " + dr["CustomerEmail"].ToString() +
                                             " - " + dr["CustomerName"].ToString());
                }
 
            }
        }
 
        protected override void OnStop()
        {
            scheduleTimer.Stop();
            eventLogEmail.WriteEntry("Stopped");
        }
        protected override void OnPause()
        {
            scheduleTimer.Stop();
            eventLogEmail.WriteEntry("Paused");
        }
        protected override void OnContinue()
        {
            scheduleTimer.Start(); ;
            eventLogEmail.WriteEntry("Continuing");
        }
        protected override void OnShutdown()
        {
            scheduleTimer.Stop();
            eventLogEmail.WriteEntry("ShutDowned");
        }
    }
}
To install the created Service – We need to add installers, to do that –
1.     Go to the Designer Pane of the Service (BirthdayEmailService) -> Right Click in the Designer Pane -> Select Add Installer.
Now in Design view for ProjectInstaller, Select serviceInstaller1.
1.     Set the ServiceName property to BirthdayEmailService. Set the StartType property to Automatic.
2.     Now Select -> serviceProcessInstaller1 -> set its Account property to LocalSystem. This will cause the service to be installed and to run on a local service account.
This completes the coding part of the Service. Now to install Service, we got to create setup project –
1.     Right Click Solution -> Add -> New Project
2.     Other Project Types -> Setup and Deployment -> Visual Studio Installer –> Select ‘Setup’ Project.
3.     Name – BirthdayEmailServiceSetup -> Click ADD.
To Add Project Output –
1.     Right Click -> BirthdayEmailServiceSetup (in solution explorer) -> Add -> Project Output
2.     Select Primary Output (along with Service name in the top DropDownList) -> click OK.
To Add Custom Actions –
1.     In Solution Explorer, right-click the setup project, Select View, and then click Custom Actions.
2.     In the Custom Actions editor, right-click the Custom Actions node and click Add Custom Action.
3.     In the opened Dialog, Double-click the Application Folder in the list to open it, select Primary Output from BirthdayEmailService(Active), and click OK.
The final Stage –
1.     Right Click the BirthdayEmailService Project in the solutions explorer, and select Set As Startup Project.
2.     Then build the BirthdayEmailService Project.
3.     Finally build the BirthdayEmailServiceSetup project too.
The complete Folder structure would be -
On successful build, Navigate to the physical location of the Setup Project (BirthdayEmailServiceSetup) to find the exe file in the Debug Folder. Take the EXE file and install it by double clicking it. On successful install, we can find the service in the Systems Services Window -
1.     Go to Control Panel –> Administrative Tools –> Services.

We can start the service by right clicking it and select start. Similarly, we can stop it, pause it and continue it. The starting action of the service is only required for initial time, later onwards on shutdown and re-start, service will be automatically started.





To find the EventLog Entries -
1.     Go to Control Panel –> Administrative Tools –> Event Viewer –> Applications and Services Logs.
There we can find our EmailLog –






EventLog logging Email Send Details – ***I tested the code with 5mins time interval.
Sample Email in the Inbox -