a quick service that sends emails

a walk through of setting up a windows service that sends emails using the EmailNotification library

December 11, 2012

As promised in my previous post I will show you how to put together a service in a matter of a few minutes using what I showed you in sending emails made easy and a quick way to create a windows service using Autofac, Quartz and Atlas. In this example I am going to wire up a service that will be powered by atlas so that it makes the service easy to install/uninstall and debug. I then use autofac to manage my IoC container. I also will use quartz for managing the schedule of my service, instead of just a standard timer. I then will use auto mapper to handle the mapping between entities. Finally I will use EmailNotification to send my emails. The final product will query a table every minute and send out emails, it will then update the table with the sent date for history tracking.

To get started you will just create a new console application. You can quickly reference all of the references to atlas, autofac, auto mapper, email notification, and quartz using nuget.

Once things are referenced lets make sure our database table and the entity that is used is wired up using auto mapper. Auto mapper takes care of the translation/mapping between my entity and the email notification MessageQueueEntity. So I will wire that up painlessly.

public class AutomapperRegistrations
{
	public void RegisterMappings()
	{
		CreateEmailMessageToMessageQueueMapping();
		CreateMessageQueueToEmailMessageMapping();
	}
	
	private void CreateEmailMessageToMessageQueueMapping()
	{
		Mapper.CreateMap<MessageQueueEntity, EmailMessage>()
			.ForMember(from => from.EmailMessageId, dest => dest.MapFrom(from => from.Identifier));
	}
	
	private void CreateMessageQueueToEmailMessageMapping()
	{
		Mapper.CreateMap<EmailMessage, MessageQueueEntity>()
			.ForMember(from => from.Identifier, dest => dest.MapFrom(from => from.EmailMessageId));
	}
}

Now I will create my autofac module which handles all the registrations into my IoC container.

public class ServiceModule : Module
{
	protected override void Load(ContainerBuilder builder)
	{
		LoadQuartz(builder);
		LoadServices(builder);
		
		builder.Register(c => new EmailRepository()).As<IEmailRepository>();
	}
	
	private static void LoadQuartz(ContainerBuilder builder)
	{
		builder.Register(c => new StdSchedulerFactory().GetScheduler()).As<IScheduler>().InstancePerLifetimeScope();
		builder.Register(c => new AutofacJobListener(ContainerProvider.Instance)).As<IJobListener>();
	}
	
	private static void LoadServices(ContainerBuilder builder)
	{
		builder.RegisterType<EmailService>().As<IAmAHostedProcess>().PropertiesAutowired();
	}
}

Now I will setup my job which processes the email(s). Since I am using Quartz it will inherit an IJob and I will implement the execute method. When Execute is called it will retrieve the unsent message from the database and send them using the power of EmailNotification.

public class SendEmailJob : IJob
{
	public IEmailRepository EmailRepository { get; set; }
	
	public void Execute(IJobExecutionContext context)
	{
		SendEmails();
	}
	
	public void SendEmails()
	{
		//Get Emails from the repository and translate them to the messagequeueentity. MessageQueueEntity is the defined email object.
		var emails = EmailRepository.GetUnsentEmails().ToMessageQueueEntities();
		
		//Use the web config configuration.
		var configuration = Master.UseAppConfig().WithEmails(emails);
		
		//The service will now send out the emails and return the results.
		var result = Master.Execute(configuration);
		
		//Set the sent date for each email that has been sent.
		foreach (var email in result.Emails.Where(e => e.IsSent))
		{
			var e = EmailRepository.GetEmail((long)email.Identifier);
			e.IsSent = email.IsSent;
			e.Sent = email.Sent;
			
			e.Save();
		}
		
		//Save any errors that were caught.
		foreach (var email in result.Emails.Where(e => !e.IsSent && e.SentException != null))
		{
			var e = EmailRepository.GetEmail((long) email.Identifier);
			e.ErrorMessage = email.SentException.Message;
			
			e.Save();
		}
	}
}

Now that we have our job I will go ahead and create the service using Quartz. Quartz will need a job that will call our SendEmailJob and a trigger. I will set the trigger to fire on an interval, every minute. You can set the trigger up many different ways, take a look at Quartz trigger options in there documentation.

public class EmailService : IAmAHostedProcess
{
	public IScheduler Scheduler { get; set; }
	public IJobListener AutofacJobListener { get; set; }
	
	public void Start()
	{
		//create the job with quartz
		var job = JobBuilder.Create<SendEmailJob>()
			.WithIdentity("SendEmail")
			.Build();
			
		//define the trigger in quartz to fire every minute
		var trigger = TriggerBuilder.Create()
			.WithIdentity("SendEmailTrigger")
			.StartNow()
			.WithCalendarIntervalSchedule(x => x.WithIntervalInMinutes(1))  //Run's every minute
			.Build();
		
		//schedule the job within quartz
		Scheduler.ScheduleJob(job, trigger);
		Scheduler.ListenerManager.AddJobListener(AutofacJobListener);
		Scheduler.Start();  //kick off the quartz job
	}
	
	public void Stop()
	{
		//shut quartz down
		Scheduler.Shutdown();
	}
	
	public void Resume()
	{
		//resume the quartz jobs
		Scheduler.ResumeAll();
	}
	
	public void Pause()
	{
		//pause the quartz jobs
		Scheduler.PauseAll();
	}
}

Finally we wire up the final piece which is to wire up the entry point of the application. I chose to use atlas since it makes it real easy for me to install/uninstall/debug a windows service. I will use the application config, tell it to use my autofac module and run my automapper registrations before atlas begins.

static class Program
{
	//Standard entry point for an application.
	static void Main(string[] args)
	{
		//Register Atlas using app.config, register the autofac module and setup any automapper registrations.
		var configuration = Host.UseAppConfig<EmailService>()
			.WithRegistrations(b => b.RegisterModule(new ServiceModule()))
			.BeforeStart(() => new AutomapperRegistrations().RegisterMappings())
			.WithArguments(args);
		
		//Kick off atlas
		Host.Start(configuration);
	}
}

Last thing we need to do is setup the app.config for both EmailNotification and atlas.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="atlas" type="Atlas.Configuration.XmlConfiguration, Atlas" />
    <section name="EmailNotificationSettings" type="EmailNotification.Config.EmailNotificationConfigurationSectionHandler, EmailNotification" allowLocation="false" allowDefinition="Everywhere" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <atlas>
    <host name="Email Service" displayName="My Sample Email Service" description="This sample email service will send emails using the email notification service." allowMultipleInstances="true">
      <runtime accounttype="networkservice" startup="automatic" />
    </host>
  </atlas>
  <EmailNotificationSettings isEnabled="true">
    <ServerSettings smtpServer="0.0.0.0" smtpServerPort="25" isSSLEnabled="false" smtpServerConnectionLimit="-1" smtpServerPassword="" smtpServerUser="" smtpServerRequiredLogin="false" useDefaultCredentials="true" />
    <TestEmailAccounts isTestEmailAccountsBlocked="false">
      <add account="mailinator.com"></add>
    </TestEmailAccounts>
  </EmailNotificationSettings>
</configuration>

That’s it were complete! Lets fire the thing up and see it fly. Remember when you go to install all you have to do is run at the command prompt Service.exe –i it will install and can be found in your windows service list using the name you set in the atlas section of your app.config. I hope you found setting up the service to be painless and easy, I’ve included the code for you to view.