ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x


Microservices architecture got its reputation in software development since it was introduced and today, it is a very popular architectural style approach to build apps and is used by many organizations. When taking a microservices approach, you would normally break product features into services or APIs to perform specific tasks, and .NET Core is a perfect candidate to build these kinds of services.
Building APIs that can be deployed and hosted anywhere is pretty much easy nowadays when .NET Core came into life. However, if you will be building a lot of APIs to support various product features within a short timeframe, then that’s how things can get troublesome. This is because each developer/team will have their own way of building APIs and has its own standard base structure of the project. You may probably have to do a lot of copy and paste to have a consistent base structure of your API project and configure its core dependencies all over again which can eat a lot of development time. For these reasons, ApiBoilerPlate was created.

The Project Template

Today, I'm happy to announce that the ApiBoilerPlate is now officially released and can now be downloaded and installed in Visual Studio 2019 from Visual Studio Market place or via .NET CLI. I made this project open-source to allow fellow developers to contribute on it for improvements.
ApiBoilerPlate is a simple yet organized project template for building ASP.NET Core APIs using .NET Core 3.x (the latest/fastest version of .NET Core to date) with preconfigured tools and frameworks. The goal is to help you get up to speed when setting up the core structure of your app and its dependencies. This enables you to focus on implementing business specific requirements without you having to copy and paste the core structure of your project, and installing its dependencies all over again. This will speed up your development time when building new API project while enforcing standard project structure with its dependencies and configurations for all your apps.
If you are looking for a project template for ASP.NET Core API that you can reuse across your team, or if you are new to ASP.NET Core and would like to get up to speed on how it works without having you to configure most of the basic features that an API will have, then this is for you.

How to get it?

There are two ways to install the template:

Tools and Frameworks Used

The template used the following tools and frameworks by default,
Keep in mind that you can always replace and choose whatever framework you want to use for your API. After all, the template is just a skeleton for your project structure with default preconfigured middlewares. For example, you can always replace Dapper with EntityFramework Core, PetaPoco, etc. and configure them yourself. You can also replace Serilog with whatever logging frameworks and providers you want that works with ASP.NET Core - the choice is yours.

Install the Template from .NET CLI

  1. Install the latest .NET Core SDK.
  2. Run dotnet new -i apiboilerplate.aspnetcore. This will install the template in your machine.
  3. Run dotnet new apiboilerplate --name "MyAPI"; -o samples. This will generate the project template named "MyAPI" within the "samples" directory.
Once installed, you should see this output below:
"The template "ASP.NET Core API Template for .NET Core 3.x" was created successfully."

Install the Template from the Visual Studio Marketplace

  1. Fire up Visual Studio 2019, click “Continue without code” link
  2. On the “Extensions” menu, click “Manage Extensions”.
  3. Click “Online” and then search for “ApiBoilerPlate”.

    ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x
  1. Click “Download”. The extension is then scheduled for install. Close all instances of Visual Studio.
  2. The VISX (Extension) installer should popup. Click “Modify” to begin installing the template as shown in the figure below,

    ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x
  1. Click “Close” after modification is complete.
Alternatively, you can download and install the VSIX Extension directly at the following link here.

Create a new Project from ApiBoilerPlate.AspNetCore Template

  1. Open Visual Studio 2019 and then select Create a New Project box
  2. The newly installed template should appear at the top. You can also type “ApiBoilerPlate” in the search bar,

    ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x
  1. Click the “ApiBoilerPlate” item and then click “Next”.
  2. Name your project to whatever you like and then click “Create”.
  3. Visual Studio should generate the files for you as shown in the figure below,

    ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x
The generated files contain the skeleton structure of the project with a very basic sample implementation for you get started working on your APIs.

Steps to run the Template

Before running the template, make sure that you build the project first to ensure that there’s no error and the project dependencies get pulled properly from NuGet. Once it’s all good, follow the steps below.
STEP 1 - Create a Test local Database
  1. Open Visual Studio 2019
  2. Go to View > SQL Server Object Explorer
  3. Drilldown to SQL Server > (localdb)\MSSQLLocalDB
  4. Right-click on the "Databases" Folder
  5. Click "Add New Database"
  6. Name it as "TestDB" and click OK
  7. Under "TestDB", Right-click on the "Tables" folder and select "Add New Table". Or you could simply right-click on the “TestDB” database, select “New Query” and run the script below to generate the “Person” table.
  8. Name the table as “Person” with the following fields,
    1. CREATE TABLE [dbo].[Person]  
    2. (  
    3.     [Id] INT NOT NULL PRIMARY KEY IDENTITY(1,1),   
    4.         [FirstName] NVARCHAR(20) NOT NULL,   
    5.         [LastName] NVARCHAR(20) NOT NULL,   
    6.         [DateOfBirth] DATETIME NOT NULL  
    7. )  
STEP 2 - Update Database ConnectionString (optional)
If you follow step 1, then you can skip this step and run the application right away.
If you have a different database and table name then you need to change the connectionString in appsettings.json that is pointing to the newly created database. You can get the connectionString values in the properties window of the "TestDB" database in Visual Studio.

Testing the Default API Controller

After setting up the database, it’s time to test the API that was preconfigured for you. Run the application and navigate to https://localhost:44321/swagger (See Properties > launchSettings.json to know how the application launching is configured)
Now, you should be presented with the Swagger UI documentation page as shown below,
ApiBoilerPlate - A Project Template For Building ASP.NET Core APIs In .NET Core 3.x 
Swagger provides advance documentation for your APIs where it allows you to look at the details of your API endpoints and test them when necessary. The template is preconfigured to enable this feature by default using Swashbuckle.AspNetCore tool. This means that every time you add API endpoints to your project, swagger will automatically generate this document without doing anything on your part. This document is very helpful as a reference especially when your APIs are publicly available and you expect many developers using it.
Another good thing about the Swagger UI is that it allows you to test your APIs. For example, you can POST a request to your API and get the response back. To see that in action, let’s make a POST request with the following body parameters,
  1. {  
  2.   "firstName""Vianne Maverich",  
  3.   "lastName""Durano",  
  4.   "dateOfBirth""2019-09-27T23:27:50.076Z"  
  5. }  
Clicking the Execute button will gives you the following response.
Response Body
  1. {  
  2.   "message""Created Successfully",  
  3.   "isError"false,  
  4.   "result": 1  
  5. }  
Response Header
  1. content-type: application/json  
  2. date: Fri, 27 Sep 2019 23:31:35 GMT  
  3. server: Microsoft-IIS/10.0  
  4. status: 201  
  5. x-powered-by: ASP.NET  
 To verify if the data was inserted into the database, you can invoke the GET method to see the results. For this example, it should return you the following JSON payload.
  1. {  
  2.   "message""Request successful.",  
  3.   "isError"false,  
  4.   "result": [  
  5.     {  
  6.       "id": 1,  
  7.       "firstName""Vianne Maverich",  
  8.       "lastName""Durano",  
  9.       "dateOfBirth""2019-09-27T23:27:50.077"  
  10.     }  
  11.   ]  
  12. }  
If you notice, the HTTP response was automatically formatted for you. The template uses AutoWrapper middleware to wrap each HTTP responses for both success and failure without doing anything on your side. For more information, see: AutoWrapper: Prettify Your ASP.NET Core APIs with Meaningful Responses.
Postman is also a good alternative to test APIs and I still use that.


Logging is also preconfigured for you. It uses Serilog.AspNetCore to log data. The template is preconfigured to log data in Console and File. You can find the logs at the Logs folder. Here’s a sample of logs that was captured based on this example, 
  1. 2019-09-27T18:08:48.0418932-05:00 [INF] () Starting web host  
  2. 2019-09-27T18:31:35.6179254-05:00 [INF] (AutoWrapper.AutoWrapperMiddleware) Request: POST https localhost:44321/api/v1/Persons {"firstName":"Vianne Maverich","lastName":"Durano","dateOfBirth":"2019-09-27T23:27:50.076Z"} Responded with [201] in 968ms  
  3. 2019-09-27T18:34:38.6852072-05:00 [INF] (AutoWrapper.AutoWrapperMiddleware) Request: GET https localhost:44321/api/v1/Persons Responded with [200] in 87ms  
To see how the log is configured, see the appsettings.logs.json file from the project template.


If you got this far, then I’m assuming you got the application up and running. At this point, you might be wondering where to start modifying the template to add your code and feel a bit confused. In this section, I’ll try my best to walk you through.

Data Folder

The first thing that you may want to look at in the template is the Data folder, which contains a class called “DBFactoryBase”. This class is an abstract class that implements a few methods to perform basic database operations,
  1. using Dapper;  
  2. using Microsoft.Extensions.Configuration;  
  3. using System.Collections.Generic;  
  4. using System.Data;  
  5. using System.Data.SqlClient;  
  6. using System.Threading.Tasks;  
  8. namespace ApiBoilerPlate1.Data  
  9. {  
  10.     public abstract class DBFactoryBase  
  11.     {  
  12.         private readonly IConfiguration _config;  
  13.         public DBFactoryBase(IConfiguration config)  
  14.         {  
  15.             _config = config;  
  16.         }  
  18.         internal IDbConnection DbConnection  
  19.         {  
  20.             get {  
  21.                 return new SqlConnection(_config.GetConnectionString("SQLDBConnectionString"));  
  22.             }  
  23.         }  
  25.         public virtual async Task<IEnumerable<T>> DbQueryAsync<T>(string sql, object parameters = null)  
  26.         {  
  27.             using (IDbConnection dbCon = DbConnection)  
  28.             {  
  29.                 dbCon.Open();  
  30.                 if (parameters == null)  
  31.                     return await dbCon.QueryAsync<T>(sql);  
  33.                 return await dbCon.QueryAsync<T>(sql, parameters);  
  34.             }  
  35.         }  
  36.         public virtual async Task<T> DbQuerySingleAsync<T>(string sql, object parameters)  
  37.         {  
  38.             using (IDbConnection dbCon = DbConnection)  
  39.             {  
  40.                 dbCon.Open();  
  41.                 return await dbCon.QueryFirstOrDefaultAsync<T>(sql, parameters);  
  42.             }  
  43.         }  
  45.         public virtual async Task<bool> DbExecuteAsync<T>(string sql, object parameters)  
  46.         {  
  47.             using (IDbConnection dbCon = DbConnection)  
  48.             {  
  49.                 dbCon.Open();  
  50.                 return await dbCon.ExecuteAsync(sql, parameters) > 0;  
  51.             }  
  52.         }  
  54.         public virtual async Task<bool> DbExecuteScalarAsync(string sql, object parameters)  
  55.         {  
  56.             using (IDbConnection dbCon = DbConnection)  
  57.             {  
  58.                 dbCon.Open();  
  59.                 return await dbCon.ExecuteScalarAsync<bool>(sql, parameters);  
  60.             }  
  61.         }  
  62.     }  
  63. }  
It uses Dapper as a data access mechanism to communicate with the database. Again, you are free to modify it as you please. The main reason why I opt for Dapper is because of its speed and simplicity. If you value convenience over performance, then you can always replace your data access with other full-blown ORM such as Entity Framework Core.

Contracts Folder

The next place to look at is the Contracts folder. This is where you add all your interfaces that are needed for your application. By default, it contains three (3) Interfaces,
The IRepository Interface
  1. namespace ApiBoilerPlate1.Contracts  
  2. {  
  3.     public interface IRepository<T>  
  4.     {  
  5.         Task<IEnumerable<T>> GetAllAsync();  
  6.         Task<T> GetByIdAsync(object id);  
  7.         Task<long> CreateAsync(T entity);  
  8.         Task<bool> UpdateAsync(T entity);  
  9.         Task<bool> DeleteAsync(object id);  
  10.         Task<bool> ExistAsync(object id);  
  11.     }  
  12. }  
We don't want our API Controller to access directly the DBFactoryBase class, instead we will let other classes handle the communication between our DBFactoryBase and API Controllers.
The code above defines a simple generic repository interface. An interface is just a skeleton of a method without the actual implementation.
The IPersonManager Interface
  1. using ApiBoilerPlate1.Domain.Entity;  
  3. namespace ApiBoilerPlate1.Contracts  
  4. {  
  5.     public interface IPersonManager : IRepository<Person>  
  6.     {  
  7.         //Add object specific methods here when necessary  
  8.     }  
  9. }  
The code above is an interface for managing the Person entity as an example. Notice that it implements the IRepository interface so that this interface can inherit all the available methods from the generic repository. This interface will be injected into the API Controller so we will only need to talk to the interface rather than the actual implementation of the repository. One of the main benefits of using the interface is to make our code reusable, testable, maintainable and taking advantage of Dependency Injection.
The IServiceRegistration Interface
  1. using Microsoft.Extensions.Configuration;  
  2. using Microsoft.Extensions.DependencyInjection;  
  4. namespace ApiBoilerPlate1.Contracts  
  5. {  
  6.     public interface IServiceRegistration  
  7.     {  
  8.         void RegisterAppServices(IServiceCollection services, IConfiguration configuration);  
  9.     }  
  10. }  
The code above is an interface that will be used for services that needs to be added in IServiceCollection.

Domain Folder

The next place to look at is the Domain folder. This is where we implement the actual database operation and its associated logic. This folder has a sub-folder called “Entity” wherein you define all your models for your database. Here’s the Person entity model example,
  1. using System;  
  3. namespace ApiBoilerPlate1.Domain.Entity  
  4. {  
  5.     public class Person  
  6.     {  
  7.         public long ID { getset; }  
  8.         public string FirstName { getset; }  
  9.         public string LastName { getset; }  
  10.         public DateTime DateOfBirth { getset; }  
  11.     }  
  12. }  
The code above is nothing but a plain class that houses a few properties. The class name represents the database table name, and the properties represents the table columns.
The PersonManager
  1. namespace ApiBoilerPlate1.Domain  
  2. {  
  3.     public class PersonManager : DBFactoryBase, IPersonManager  
  4.     {  
  5.         public PersonManager(IConfiguration config) : base(config)  
  6.         {  
  8.         }  
  9.         public async Task<IEnumerable<Person>> GetAllAsync()  
  10.         {  
  11.             return await DbQueryAsync<Person>("SELECT * FROM Person");  
  12.         }  
  14.         public async Task<Person> GetByIdAsync(object id)  
  15.         {  
  16.             return await DbQuerySingleAsync<Person>("SELECT * FROM Person WHERE ID = @ID"new { id });  
  17.         }  
  19.         public async Task<long> CreateAsync(Person person)  
  20.         {  
  21.             string sqlQuery = $@"INSERT INTO Person (FirstName, LastName, DateOfBirth)   
  22.                                      VALUES (@FirstName, @LastName, @DateOfBirth)  
  23.                                      SELECT CAST(SCOPE_IDENTITY() as bigint)";  
  25.             return await DbQuerySingleAsync<long>(sqlQuery, person);  
  26.         }  
  27.         public async Task<bool> UpdateAsync(Person person)  
  28.         {  
  29.             string sqlQuery = $@"IF EXISTS (SELECT 1 FROM Person WHERE ID = @ID)   
  30.                                             UPDATE Person SET FirstName = @FirstName, LastName = @LastName, DateOfBirth = @DateOfBirth  
  31.                                             WHERE ID = @ID";  
  33.             return await DbExecuteAsync<bool>(sqlQuery, person);  
  34.         }  
  35.         public async Task<bool> DeleteAsync(object id)  
  36.         {  
  37.             string sqlQuery = $@"IF EXISTS (SELECT 1 FROM Person WHERE ID = @ID)  
  38.                                         DELETE Person WHERE ID = @ID";  
  40.             return await DbExecuteAsync<bool>(sqlQuery, new { id });  
  41.         }  
  42.         public async Task<bool> ExistAsync(object id)  
  43.         {  
  44.             return await DbExecuteScalarAsync("SELECT COUNT(1) FROM Person WHERE ID = @ID"new { id });  
  45.         }  
  46.     }  
  47. }  
The code above is a concrete class that implements the DBFactorBase base class and IPersonManager interface that we have just created earlier. This is where you implement domain specific log and database operations.

DTO Folder

Data Transfer Object (a.k.a DTO) are classes that define the model with predefined validation in place. DTOs will be passed as the parameters to your `API` endpoints or use it as a return object for results. The basic idea about having a DTO is to decouple the validation from the Entity models and control the data that you want to allow the client to see.
Here’s an example of the PersonDTO,
  1. using FluentValidation;  
  2. using System;  
  4. namespace ApiBoilerPlate1.DTO  
  5. {  
  6.     public class PersonDTO  
  7.     {  
  8.         public string FirstName { getset; }  
  9.         public string LastName { getset; }  
  10.         public DateTime DateOfBirth { getset; }  
  11.     }  
  13.     public class PersonDTOValidator : AbstractValidator<PersonDTO>  
  14.     {  
  15.         public PersonDTOValidator()  
  16.         {  
  17.             RuleFor(o => o.FirstName).NotEmpty();  
  18.             RuleFor(o => o.LastName).NotEmpty();  
  19.             RuleFor(o => o.DateOfBirth).NotEmpty();  
  20.         }  
  21.     }  
  22. }  
The class above defines validations for the model. It uses FluentValidation.AspNetCore to provide a clean, flexible and strongly-typed validation rules.

Helpers Folder

The next place to look at is the Helpers folder. This is where you add any class that performs common tasks. For example, you can add Utility or Extension classes in this folder. By default, it contains an Extensions folder and a MapperProfile class.
The MappingProfile Class
  1. using AutoMapper;  
  2. using ApiBoilerPlate1.Domain.Entity;  
  3. using ApiBoilerPlate1.DTO;  
  5. namespace ApiBoilerPlate1.Helpers  
  6. {  
  7.     public class MappingProfile : Profile  
  8.     {  
  9.         public MappingProfile()  
  10.         {  
  11.             CreateMap<Person, PersonDTO>().ReverseMap();  
  12.         }  
  13.     }  
  14. }  
The class above is where you define the mappings between your DTOs and Entity models. It uses AutoMapper to map between objects. In this example, we created a mapping between a Person entity and a PersonDTO model.

The Extensions Folder

This folder is where you add all classes for your extension methods in your project. By default, it contains a ServiceRegistrationExtension class with an extension method AddServicesInAssembly() which is responsible for adding all available services within the assembly. This class will be called in the ConfigureServices() method of the Startup.cs file.
Here’s the ServiceRegistrationExtension implementation,
  1. namespace ApiBoilerPlate1.Helpers.Extensions  
  2. {  
  3.     public static class ServiceRegistrationExtension  
  4.     {  
  5.         public static void AddServicesInAssembly(this IServiceCollection services, IConfiguration configuration)  
  6.         {  
  7.             var appServices = typeof(Startup).Assembly.ExportedTypes  
  8.                             .Where(x => typeof(IServiceRegistration)  
  9.                             .IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract)  
  10.                             .Select(Activator.CreateInstance)  
  11.                             .Cast<IServiceRegistration>().ToList();  
  13.             appServices.ForEach(svc => svc.RegisterAppServices(services, configuration));  
  14.         }  
  15.     }  
  16. }  

The Installers Folder

This is where you add services that you would want to be configured in the application startup. By default, it contains two classes.
The RegisterContractMappings Class
  1. namespace ApiBoilerPlate1.Installers  
  2. {  
  3.     public class RegisterContractMappings : IServiceRegistration  
  4.     {  
  5.         public void RegisterAppServices(IServiceCollection services, IConfiguration configuration)  
  6.         {  
  7.             services.AddTransient<IPersonManager, PersonManager>();  
  8.         }  
  9.     }  
  10. }  
The class above is responsible for registering all interface mappings between your contract repositories and concrete classes that implement the contracts.
The RegisterModelValidators Class
  1. namespace ApiBoilerPlate1.Installers  
  2. {  
  3.     public class RegisterModelValidators : IServiceRegistration  
  4.     {  
  5.         public void RegisterAppServices(IServiceCollection services, IConfiguration configuration)  
  6.         {  
  7.             services.AddTransient<IValidator<PersonDTO>, PersonDTOValidator>();  
  8.         }  
  9.     }  
  10. }  
The class above is responsible for registering all validators for your DTO models.
Notice that both classes above implement the IServiceRegistration interface so that they will be configured automatically upon calling the AddServicesInAssembly() extension method defined in ServiceRegistrationExtension class.

API Folder

Now that we have most of the pieces in place, the next place to look at is the API folder. This folder contains a sub-folder called “v1” to organize Controllers into versions. By default, the v1 folder contains the PersonsController with the following implementation,
  1. namespace ApiBoilerPlate1.API.v1  
  2. {  
  3.     [Route("api/v1/[controller]")]  
  4.     [ApiController]  
  5.     public class PersonsController : ControllerBase  
  6.     {  
  7.         private readonly ILogger<PersonsController> _logger;  
  8.         private readonly IPersonManager _personManager;  
  9.         private readonly IMapper _mapper;  
  10.         public PersonsController(IPersonManager personManager, IMapper mapper, ILogger<PersonsController> logger)  
  11.         {  
  12.             _personManager = personManager;  
  13.             _mapper = mapper;  
  14.             _logger = logger;  
  15.         }  
  17.         [HttpGet]  
  18.         public async Task<IEnumerable<Person>> Get()  
  19.         {  
  20.             return await _personManager.GetAllAsync();  
  21.         }  
  23.         [Route("{id:long}")]  
  24.         [HttpGet]  
  25.         public async Task<Person> Get(long id)  
  26.         {  
  27.             var person = await _personManager.GetByIdAsync(id);  
  28.             if (person != null)  
  29.             {  
  30.                 return person;  
  31.             }  
  32.             else  
  33.                 throw new ApiException($"Record with id: {id} does not exist.", 204);  
  34.         }  
  36.         [HttpPost]  
  37.         public async Task<ApiResponse> Post([FromBody] PersonDTO dto)  
  38.         {  
  40.             if (ModelState.IsValid)  
  41.             {  
  42.                 try  
  43.                 {  
  44.                     var person = _mapper.Map<Person>(dto);  
  45.                     return new ApiResponse("Created Successfully", await _personManager.CreateAsync(person), 201);  
  46.                 }  
  47.                 catch (Exception ex)  
  48.                 {  
  49.                     _logger.Log(LogLevel.Error, ex, "Error when trying to insert.");  
  50.                     throw;  
  51.                 }  
  52.             }  
  53.             else  
  54.                 throw new ApiException(ModelState.AllErrors());  
  55.         }  
  57.         [Route("{id:long}")]  
  58.         [HttpPut]  
  59.         public async Task<ApiResponse> Put(long id, [FromBody] PersonDTO dto)  
  60.         {  
  61.             if (ModelState.IsValid)  
  62.             {  
  63.                 try  
  64.                 {  
  65.                     var person = _mapper.Map<Person>(dto);  
  66.                     person.ID = id;  
  68.                     if (await _personManager.UpdateAsync(person))  
  69.                         return new ApiResponse("Update successful."true);  
  70.                     else  
  71.                         throw new ApiException($"Record with id: {id} does not exist.", 400);  
  72.                 }  
  73.                 catch (Exception ex)  
  74.                 {  
  75.                     _logger.Log(LogLevel.Error, ex, "Error when trying to update with ID:{@ID}", id);  
  76.                     throw;  
  77.                 }  
  78.             }  
  79.             else  
  80.                 throw new ApiException(ModelState.AllErrors());  
  81.         }  
  84.         [Route("{id:long}")]  
  85.         [HttpDelete]  
  86.         public async Task<bool> Delete(long id)  
  87.         {  
  88.             try  
  89.             {  
  90.                 var isDeleted = await _personManager.DeleteAsync(id);  
  91.                 if (isDeleted)  
  92.                 {  
  93.                     return isDeleted;  
  94.                 }  
  95.                 else  
  96.                     throw new ApiException($"Record with id: {id} does not exist.", 400);  
  97.             }  
  98.             catch (Exception ex)  
  99.             {  
  100.                 _logger.Log(LogLevel.Error, ex, "Error when trying to delete with ID:{@ID}", id);  
  101.                 throw;  
  102.             }  
  104.         }  
  105.     }  
  106. }  
The class above defines the API endpoints and its routes with preconfigured implementation as an example. For more information about creating APIs ASP.NET Core, see: Create web APIs with ASP.NET Core.

Workers Folder

As an additional feature, the template includes a default example on how to run background tasks within ASP.NET Core. Here’s the simple implementation of a worker service,
  1. namespace ApiBoilerPlate1.Workers  
  2. {  
  3.     public class MessageQueueProcessorService : BackgroundService  
  4.     {  
  5.         private readonly ILogger<MessageQueueProcessorService> _logger;  
  7.         public MessageQueueProcessorService(ILogger<MessageQueueProcessorService> logger)  
  8.         {  
  9.             _logger = logger;  
  10.         }  
  11.         protected override async Task ExecuteAsync(CancellationToken stoppingToken)  
  12.         {  
  13.             _logger.LogDebug($"MessageQueueProcessorService is starting.");  
  15.             stoppingToken.Register(() => _logger.LogDebug($" MessageQueueProcessorService background task is stopping."));  
  17.             while (!stoppingToken.IsCancellationRequested)  
  18.             {  
  19.                 _logger.LogDebug($"MessageQueueProcessorService task doing background work.");  
  21.                 //TO DO:  
  22.                 //PubSub/Message Queue subcription and process message  
  23.                 //Save to DB  
  25.                 await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);  
  26.             }  
  28.             _logger.LogDebug($"MessageQueueProcessorService background task is stopping.");  
  29.         }  
  30.     }  
  31. }  
The code above simulates a simple background task that keeps on running every 5 seconds. So, if there’s a need for you to implement a worker service, then it should provide you an idea on how to get started.
For more information about running background tasks with hosted services in ASP.NET Core, See Hosted Services in ASP.NET Core.
To see how each of the services in the template is configured, then take a look at the Startup.cs and Program.cs classes. To give you a quick overview, here’s how the ConfigureServices() method of Startup.cs file looks like,
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     //Uncomment to Register Worker Service  
  4.     //services.AddSingleton<IHostedService, MessageQueueProcessorService>();  
  6.     //Register DTO Validators and Interface Mappings for Repositories  
  7.     services.AddServicesInAssembly(Configuration);  
  9.     //Disable Automatic Model State Validation built-in to ASP.NET Core  
  10.     services.Configure<ApiBehaviorOptions>(opt => { opt.SuppressModelStateInvalidFilter = true; });  
  12.     //Register MVC/Web API and add FluentValidation Support  
  13.     services.AddControllers()  
  14.             .AddFluentValidation(fv => {  fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false; });  
  16.     //Register Automapper  
  17.     services.AddAutoMapper(typeof(Helpers.MappingProfile));  
  19.     //Register Swagger  
  20.     services.AddSwaggerGen(c =>  
  21.     {  
  22.         c.SwaggerDoc("v1"new OpenApiInfo { Title = "ASP.NET Core Template API", Version = "v1" });  
  23.     });  
  24. }  
For more information on how Startup and Program class works, read: ASP.NET Core Fundamentals.


In this post, we’ve learned how to use the ApiBoilerPlate project template for building APIs in .NET Core 3.0. We've also learned about its structure and how it was implemented.
I’m pretty sure there are still lots of things to improve in this project, so feel free to try it out and let me know your thoughts. Comments and suggestions are welcome, please drop a message and I’d be happy to answer any queries as I can.
Source code can be found here.