Updated: Oct 1, 2019
In this post we will be talking about how to use Hosted Services to create background tasks, to explain this we will provide the use case of a company with scheduled promotions and how they can automate email sending to let their customers know the offers available.
What are background tasks?
A background task is a computer process that runs behind the scenes, without human intervention. With it, you can achieve any task that needs to run backstage, an example of this could be:
Poll a database to look for changes
How can I create background tasks with .NET Core?
Hosted services are basically regular classes with logic that are hosted in your application. So the advantages are:
Run tasks in the background
No need for complex architecture
No need to create separate services
Easy to implement
Interface driven development
If you're already experienced and are only interested in the Hosted Service implementation, please jump to Step 6.
If you are interested in knowing from how to create a dotnet application to how do I implement Background tasks in .NET Core, then go ahead from the beginning.
This sample project to show you how hosted services work in .NET Core; we will call it AccioEmail. It’s a background task in our web application that will send an email when one of the scheduled promotions is close to be launched.
Creating our project
Once .NET Core SDK it's installed, you should be able to access through command-line tool. Open your preferred command-line tool and invoke the terminal:
Now that we made sure that dotnet it's installed in our computer, let's proceed to create a solution file for our project.
Creating a solution file
A solution is a container that's used to organize one or more related code projects. - Learn about projects and solutions, Microsoft Docs.
1. Create a directory for the project, let's call it ACCIOEmail.
2. Run dotnet's command to create a new solution, sln is short for solution, this will create an ACCIOEmail.sln
Creating our projects backbone
This project will consist of 3 layers:
Data Layer: the main purpose of a Data Layer it's to hold all the Data logic, this could be any connection to the database; data mocks (like the case of this project); and anything related to data.
Business Logic Layer: the business logic contains all the business rules, and will serve as a middle-ware between the data layer and the views/client.
API: an API, as its name states, an Application Programming Interface, which purpose it's a set of functions and protocols that serves as an interface between a server and a client.
Let's add those 3 new projects to our solution created above.
1. Let's create our Data Layer and our Business Layer.
A class library defines types and methods that are called by an application.
A classlib it's nothing more than a group of files with a purpose that we contain in a separate project, it's not an executable.
2. Let's proceed to create our API project.
3. Let's add the created projects into our solution. Besides solution being a way to organize your projects, it also helps with the Intellisense in Visual Studio Code.
This is how our project's structure will look:
Creating Data Structure
1. Let's do a change directory to our Data Layer. Then let's create a simple model to reflect the structure of our data.
.NET Core CLI still does not support creating files into a project by using the commands. You can either create the files manually, or add the C# Extensions extension by jchannon, which allows you to create files with the right namespaces by just right clicking the desired folder.
2. Let's create a folder called Models, let's right click on top of it and create a class called Promotions.cs. This class will contain the basic info of our promotions object.
3. Let's create our mock data.
Create a new directory called Repository, inside it we will create an interface (IPromotionsRepository.cs) with a call to query all the promotions, and the implementation of it (PromotionsRepository.cs).
GetPromotions will contain a simple list to simulate the data, in case you're implementing an ORM, or a Database connection, here's is where you do all the queries to the database. In our case, we're just going to simulate the data.
Configuring our Email using MailKit
MailKit is a cross-platform mail client library built on top of MimeKit.
A few years ago, Microsoft notified that SmtpClient Class it's obsolete; and suggested to either use MimeKit or MailKit which is built on top of MimeKit.
1. Install MailKit using .NET Core CLI
2. In our BLL project, let's create a directory called Services, inside it let's create our EmailService interface and implementation.
3. In our IEmailService.cs, let's create a method called SendEmailAsync, which will receive 3 main parameters:
Recipient email address
Cc email address [optional]
Now let's implement our EmailService.
Build your MimeMessage object. The MimeMessage it's the object that represents the email, this needs to be initialized with the Sender information: SendersName, and SmtpUserName which normally is the sender's email address.
Configure Email's body. The Email body it's the main content of the body, which supports multiple formats, two of them being Plain Text and HTML. In case you need the email to be Plain Text, just send TextFormat.Plain to TextPart instance.
Add the recipients. Recipients are the ones receiving our emails, you can specify more than one by setting an array of strings, check toAddressList. After converting our parameter into an array of strings I can iterate through it and add the multiple recipients to our MimeMessage Object.
Setup your Email Settings. In order to send emails you need to provide the SMTP server that will allow you to communicate via email with other servers. In this case I used Gmail's server.
SMTP Server: smtp.gmail.com
Server PORT: 587
Send Email. Let's put everything together and proceed to send our email.
socketOption will determine the type of connection, if it's secure or not; that depends on whether or not the SMTP server you're using supports a secure connection.
Configuring our Background Email Sender using IHostedServices
1. Let's create an interface called IBackgroundEmailSender, which will contain the method we will use in our background task.
2. Implement the IBackgroundEmailSender interface. DoWork will act as a worker reading our data results and filtering those promotions starting soon. If there's promotions soon to be launched, then it will proceed to send an email.
3. Create the Background Service. The background service as I mentioned before it's very easy to setup.
Let's create our Hosted Service consumer, let's call it ConsumeScopedHostedService. In order to use the hosted services functionalities, we need to implement .NET Core's IHostedService interface.
When we implement IHostedService interface, there are 2 main methods we need to configure:
StartAsync: contains the logic to initiate the background task. In our case, we are calling the DoWork method, who at the same time it's calling our BackgroundEmailSender implementation, which is the one in charge of sending the emails to our customers.
StopAsync: contains the logic to end a background task. When the task is completed successfully, this is triggered.
DoWork: this method does not belong to IHostedService; it's just a method that creates a scope to resolve the scoped background task service to call its DoWork method . You might as well create your logic in your background consumer class, it's just a way of keeping things organized and in its place.
4. Update your Startup.cs: Before running our app, we need to configure our Startup.cs to let our application know which is our hosted service consumer by using the AddHostedService extension method and to declare our scoped classes and interfaces.
5. Run the app
And we got mail!
Hosted Services can be really helpful when you need to achieve small functionalities of automation inside your running application, you may need to evaluate if what you need can be solved by using Hosted Services.
Hope this was of help, to understand how hosted services work and how they can be implemented.
Get source code