Introduction
This article demonstrates a real-world solution. It is my second article explaining Windows Services. If you are not familiar with Windows services on the .NET Framework, you can read my first article, "Windows Services in Action I".
This service will track the hours of a day. It will read the database if the time matches the hour specified in the config file. It will send an e-mail if there is(are) matching date(s).
Background
In this article, you can find many details of coding like reading from config.file, reading from a database, using a timer control, and sending an e-mail in HTML format.
Using the Code
Creating Event Logs
Having an event log in hand is very useful to track our service. So first, let’s create one.
(If you want to make life easier, you can download code from the previous article now. There is already an Eventlog
control there.)
It can be dragged-and-dropped from the toolbox into SimpleService.cs (Design mode).
Add codes to create an Eventlog
control and its detail. After that, you will have the code block below:
public SimpleService()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("SimpleSource"))
System.Diagnostics.EventLog.CreateEventSource("SimpleSource", "SimpleLog");
eventLogSimple.Source = "SimpleSource";
eventLogSimple.Log = "SimpleLog";
}
Configuration File
Because there isn't a regular UI, we can use a config file to pass any parameters via the config file. So you can simply add a config file by right-clicking the Project in the Solution Explorer Visual Studio 2005. Click Add, New Item and Application Configuration File, then click OK. After that, you will see the app.config file in your project section. Double click to open it and add the following lines in the <appSettings>
section:
<appSettings>
<!-- example value : key="Hour" value="09:00"-->
<add key="Hour" value="10:00"/>
</appSettings>
This means our service will read the data and send emails at 10:00
. (I am assuming the time settings are based on 0-24-hours.)
Reading Config File
Let's do this job in a simplistic way. At first, we must add the System.Configuration
library adding the line below:
using System.Configuration;
Then let’s create a function reading from the config file. The function looks like below:
private string ReadConfigFile()
{
System.Configuration.AppSettingsReader appsreader
=new AppSettingsReader();
String value = String.Empty;
value = (String)appsreader.GetValue("Hour", value.GetType());
return value.ToString();
}
Reading Data
We can use an Access table named bd.mdb and use OldeDb Provider of the .NET Framework. So we must add using System.Data.OleDb;
to the top of SimpleService.cs. We must also create connection string, and query in a while
loop and send mail to the person(s) who was(were) born on the day. Sending mail is achieved by another function which is explained shortly.
Here is the code block:
private bool ReadDataFromDB()
{
string sPName = "";
string sPSurname = "";
string sPEmail = "";
string strMonth = DateTime.Now.Month.ToString();
string strDay = DateTime.Now.Day.ToString();
string sQuery = "Select * from BirthDays where Days='" +
strDay + "' and Months='" + strMonth + "';";
string sConnString = @"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=c:\bd.mdb";
eventLogSimple.WriteEntry("ReadDataFromDB() called...");
eventLogSimple.WriteEntry(sQuery);
OleDbConnection DBConn = new OleDbConnection(sConnString);
OleDbCommand DBComm = new OleDbCommand(sQuery);
DBComm.CommandType = CommandType.Text;
DBComm.Connection = DBConn;
try
{
eventLogSimple.WriteEntry("In try...");
DBConn.Open();
OleDbDataReader DBReader = DBComm.ExecuteReader();
while (DBReader.Read())
{
eventLogSimple.WriteEntry("In while...");
sPName = DBReader.GetValue(2).ToString();
sPSurname = DBReader.GetValue(1).ToString();
sPEmail = DBReader.GetValue(5).ToString();
SendMailToStaff(sPEmail, sPName + " " + sPSurname);
SendMailToWebAdmin("admin@company.com",
"Birthday service sent e-mail to: "+ sPName + " " + sPSurname);
eventLogSimple.WriteEntry
("E-mail: " + DBReader.GetValue(5).ToString()
+ " - Staff Name: "+sPName + " " + sPSurname);
}
}
catch
{
eventLogSimple.WriteEntry("An error has occurred!");
}
finally
{
DBConn.Close();
}
return true;
}
Creating a Timer
Now we need a timer which will decide when to act:
private System.Timers.Timer mytimer = null;
And then we must create a timer
object in the OnStart
. It will look like below:
protected override void OnStart(string[] args)
{
eventLogSimple.WriteEntry("Simple service started!");
sTimeToSend = ReadConfigFile();
eventLogSimple.WriteEntry("Time to send: " + sTimeToSend);
MyTimer = new System.Timers.Timer(3600000);
MyTimer.Elapsed += new System.Timers.ElapsedEventHandler
(this.ServiceTimer_Tick);
MyTimer.Enabled = true;
}
Then, let's create a Tick
event:
private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
MyTimer.Enabled = false;
string sCurrentHour = DateTime.Now.Hour.ToString() + ":00";
DateTime dCurrentHour = Convert.ToDateTime(sCurrentHour);
DateTime dTimeToSend = Convert.ToDateTime(sTimeToSend);
int iComparison = DateTime.Compare(dTimeToSend, dCurrentHour);
if (iComparison == 0)
ReadDataFromDB();
MyTimer.Enabled = true;
}
Sending E-mails
To send mail, we will use the System.Net.Mail;
library so we must add to the top again:
using System.Net.Mail;
The service will send an informative email to the Web admin.
private bool SendMailToWebAdmin(string sTo, string sText)
{
SmtpClient SClient = new SmtpClient();
System.Net.Mail.MailMessage msgMail = new System.Net.Mail.MailMessage();
SClient.Host = "10.1.1.28";
SClient.Port = 25;
MailAddress strFrom = new MailAddress("xyx@xyzmail.com");
string strTo = sTo;
string strSubject = "Corporate Portal Service -
Birthday Congratulation - (Monitoring) ";
string strEmailBody = sText;
msgMail.Body = strEmailBody;
msgMail.Subject = strSubject;
msgMail.To.Add(strTo);
msgMail.From = strFrom;
msgMail.IsBodyHtml = true;
if (strTo.Trim() != "")
{
try
{
SClient.Send(msgMail);
eventLogSimple.WriteEntry("Sent");
return true;
}
catch
{
eventLogSimple.WriteEntry("Failed");
return false;
}
}
return false;
}
Mails that we are sending to people is achieved by another function which sends HTML content. Unfortunately, I could not place HTML here. I usually send a very colourful page.
private bool SendMailToStaff(string sTo, string sName)
{
SmtpClient SClient = new SmtpClient();
System.Net.Mail.MailMessage msgMail = new System.Net.Mail.MailMessage();
SClient.Host = "10.1.1.28";
SClient.Port = 25;
MailAddress strFrom = new MailAddress("xyx@xyzmail.com");
string strTo = sTo;
string strSubject = "Happy Birthday "+sName+"!";
string strEmailBody = @"HTML STRING COMES HERE";
msgMail.Body = strEmailBody;
msgMail.Subject = strSubject;
msgMail.To.Add(strTo);
msgMail.From = strFrom;
msgMail.IsBodyHtml = true;
if (strTo.Trim() != "")
{
try
{
SClient.Send(msgMail);
eventLogSimple.WriteEntry("Sent");
return true;
}
catch
{
eventLogSimple.WriteEntry("Failed");
return false;
}
}
return false;
}
Points of Interest
I think using services is the best choice in our case. If you think that I am wrong, please let me know.
History
- 2007-12-28: This is the 4th release of the service which has been running for 2 years.