You are on page 1of 20

How To Build Your First .

NET
Microservice
Quick Start Guide

Julio Casal 7/12/21 dotnetmicroservices.com


Introduction
This guide will show you how to build your first microservice with .NET step by step. By the end of the
guide, you will have a fully working .NET 5 microservice with a functional REST API and its own NoSQL
database.

Get the tools


Besides a computer with Windows, macOS or Linux, you only need three things to follow the guide:

1. The .NET 5 SDK


• Get it here: https://dotnet.microsoft.com/download/dotnet/5.0
• Get the SDK download, not the Runtime
• Use the latest version available

2. Docker desktop, available at https://docs.docker.com/get-docker

3. Visual Studio Code, available at https://aka.ms/vscode

Create the project


Open your favorite terminal according to your OS, choose a directory for your new project and type this:

dotnet new webapi -n CatalogService --no-https

That will:

• Generate a few files for a new REST API style project in a new CatalogService directory
• Use CatalogService as the namespace for all C# generated files
• Removes HTTPS enforcement (to keep things simple)

Open VS Code
To start working with the generated files in VS Code, do this:

1. Change dirs to CatalogService

2. Type this to open VS Code at your current directory (notice the dot at the end):

code .

3. Get ready to start coding!

Julio Casal 7/12/21 dotnetmicroservices.com


Install the C# extension
The VS Code C# extension provides many enhancements to your C# coding experience.

To install the C# extension:

1. Click on the Extensions icon in the Activity Bar (on the left side)

2. Search for C#

3. Install the C# for Visual Studio Code extension

Julio Casal 7/12/21 dotnetmicroservices.com


4. After installation, this dialog should popup. Click Yes:

Build the project


Let’s build the project to verify things are working properly.

To build the project:

1. Open a Terminal in VS Code by using the Terminal → New Terminal menu item.

2. Type this:

dotnet build

3. You should get a Build succeeded message:

Delete unneeded files


You can now remove a few files you won’t need for your new project. Delete these files using the
Explorer on the left side:

1. Controllers\WeatherForecastController.cs
2. WeatherForecast.cs

Add the entity class


Since our microservice will manage catalog items, we need a class to represent those items in our
upcoming database.

To create the CatalogItem entity class:

1. Create a new folder called Entities by right clicking on any empty space on the VS Code Explorer and
selecting New Folder

Julio Casal 7/12/21 dotnetmicroservices.com


2. Right click the Entities folder, select New File and enter CatalogItem.cs

3. Add this code to the new file:

using System;

namespace CatalogService.Entities
{
public class CatalogItem
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public DateTimeOffset CreatedDate { get; set; }
}
}

Create the repository interface


The repository interface will be used by our upcoming controller class to query and store items in the
database. The interface only describes what methods are available to interact with the database, but
not how they are implemented. We will implement them in a future section.

To create the ICatalogItemRepository interface:

1. Add a Repositories directory at the CatalogService root

2. Add an ICatalogItemRepository.cs file under the Repositories directory

3. Add this code to the file:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CatalogService.Entities;

namespace CatalogService.Repositories
{
public interface ICatalogItemRepository
{
Task CreateAsync(CatalogItem entity);
Task<IReadOnlyCollection<CatalogItem>> GetAllAsync();
Task<CatalogItem> GetAsync(Guid id);
}
}

The interface has three methods:

• CreateAsync. Creates a new catalog item.

Julio Casal 7/12/21 dotnetmicroservices.com


• GetAllAsync. Gets all the catalog items in the database.
• GetAsync. Gets a single item from the database based on an id.

Add the Data Transfer Object classes


Data Transfer Objects (or DTOs) are classes used to either receive data into your microservice’s REST API
or to send data back to the caller. You don’t want to use your entity classes for this since that would
expose your database internals and make things harder for you and your service consumers later.

To create the DTOs:

1. Add a Dtos.cs file at the at the CatalogService root

2. Add this code to the file:

using System;
using System.ComponentModel.DataAnnotations;

namespace CatalogService.Dtos
{
public record CatalogItemDto(
Guid Id,
string Name,
string Description,
decimal Price,
DateTimeOffset CreatedDate);

public record CreateCatalogItemDto(


[Required] string Name,
string Description,
[Range(0, 1000)] decimal Price);
}

Notice the following:

1. We added two DTOs:


• CatalogItemDto. Used to return catalog item data to our REST API callers
• CreateCatalogItemDto. Used to receive catalog item data from our callers

2. The CreateCatalogItemDto is using the Required and Range data annotations to ensure we never
receive an empty Name and to ensure the Price is always a value between 0 and 1000.

Add the AsDto extension method


Any time our controller needs to retrieve data from our repository and send it back to our REST API
caller it will need to transform entities into DTOs. Let’s add a simple extension method that can perform
the transformation, so the controller doesn’t have to deal with the transformation details.

To add the AsDto extension method:

Julio Casal 7/12/21 dotnetmicroservices.com


1. Add an Extensions.cs file at the CatalogService root.

2. Add this code to the file:

using CatalogService.Dtos;
using CatalogService.Entities;

namespace CatalogService
{
public static class Extensions
{
public static CatalogItemDto AsDto(this CatalogItem item)
{
return new CatalogItemDto(
item.Id,
item.Name,
item.Description,
item.Price,
item.CreatedDate);
}
}
}

Add the CatalogItem controller


The controller is the class that groups the set of actions that can handle the REST API requests.

To add the CatalogItemController:

1. Add a CatalogItemsController.cs file under the Controllers directory

2. Add this code to the file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CatalogService.Dtos;
using CatalogService.Entities;
using CatalogService.Repositories;
using Microsoft.AspNetCore.Mvc;

namespace CatalogService.Controllers
{
[ApiController]
[Route("items")]
public class CatalogItemsController : ControllerBase
{
private readonly ICatalogItemRepository repository;

public CatalogItemsController(ICatalogItemRepository repository)


{
this.repository = repository;
}

Julio Casal 7/12/21 dotnetmicroservices.com


[HttpGet]
public async Task<IEnumerable<CatalogItemDto>> GetAsync()
{
var itemEntities = await repository.GetAllAsync();
var itemDtos = itemEntities.Select(entity => entity.AsDto());
return itemDtos;
}

[HttpGet("{id}")]
public async Task<ActionResult<CatalogItemDto>> GetByIdAsync(Guid id)
{
var item = await repository.GetAsync(id);

if (item == null)
{
return NotFound();
}

return item.AsDto();
}

[HttpPost]
public async Task<ActionResult<CatalogItemDto>>
PostAsync(CreateCatalogItemDto createItemDto)
{
var item = new CatalogItem
{
Name = createItemDto.Name,
Description = createItemDto.Description,
Price = createItemDto.Price,
CreatedDate = DateTimeOffset.UtcNow
};

await repository.CreateAsync(item);

return CreatedAtAction(
nameof(GetByIdAsync),
new { id = item.Id }, item);
}
}
}

About CatalogItemsController:

• It is configured to map to the /items base route via the Route attribute.

• It receives an instance of the catalog items repository via dependency injection in its constructor.
This is a technique for decoupling classes making our code easier to modify and reuse later.

• The GetAsync and GetByIdAsync methods use the AsDto extension method to map the items
received from the repository to the returned DTOs.

• The GetByIdAsync method returns a 404 (Not Found) error if it can’t find the item.

• After creating the item via the repository, the PostAsync method uses the CreatedAtAction method
to return a 201 (Created) response that includes the route where the created item can be found.

Julio Casal 7/12/21 dotnetmicroservices.com


Add the MongoDB driver NuGet package
For our microservice to talk to MongoDB we need the MongoDB.Driver nuget package.

To add the MongoDB.Driver nuget package:

1. Open your Terminal in VS Code

2. Type this:

dotnet add package MongoDB.Driver

Implement the CatalogItemRepository


It’s time to implement the logic to connect to MongoDB, retrieve items from our database and create
new items on it. This all goes into a new CatalogItemRepository class.

To create the CatalogItemRepository class:

1. Add a new CatalogItemRepository.cs file under the Repositories directory

2. Add this code to the file:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using CatalogService.Entities;
using MongoDB.Driver;

namespace CatalogService.Repositories
{
public class CatalogItemRepository : ICatalogItemRepository
{
private const string collectionName = "catalogitems";
private readonly IMongoCollection<CatalogItem> dbCollection;
private readonly FilterDefinitionBuilder<CatalogItem> filterBuilder =
Builders<CatalogItem>.Filter;

public CatalogItemRepository(IMongoDatabase database)


{
dbCollection = database.GetCollection<CatalogItem>(
collectionName);
}

public async Task<IReadOnlyCollection<CatalogItem>> GetAllAsync()


{
return await dbCollection.Find(filterBuilder.Empty)
.ToListAsync();
}

public async Task<CatalogItem> GetAsync(Guid id)


{
FilterDefinition<CatalogItem> filter =
filterBuilder.Eq(catalogItem => catalogItem.Id, id);
return await dbCollection.Find(filter).FirstOrDefaultAsync();

Julio Casal 7/12/21 dotnetmicroservices.com


}

public async Task CreateAsync(CatalogItem catalogItem)


{
if (catalogItem == null)
{
throw new ArgumentNullException(nameof(catalogItem));
}

await dbCollection.InsertOneAsync(catalogItem);
}
}
}

Notice the following in the code above:

• CatalogItemRepository implements ICatalogItemRepository, so it can be used anywhere that


interface is used, like our controller is doing.

• A MongoDB collection named catalogitems will be created in the database to hold the catalog
items. This is like a table in relational databases.

• Dependency injection is used again to provide an IMongoDatabase instance to


CatalogItemRepository via its constructor.

• The GetAllAsync and GetAsync methods use the Find method in the dbCollection object to find
catalog items in the catalogitems collection using filters.

• GetAsync returns null if it can’t find the specified catalog item.

• The CreateAsync method uses the InsertOneAsync method in the dbCollection object to create new
items in the catalogitems collection.

Add the MongoDB connection details


Our microservice needs to know the location and port of the MongoDB server. We will stand up the
server itself in a future section, but for now let’s specify the MongoDB host and port via configuration.

To add MongoDB connection details:

1. Open the appsettings.json file found at the CatalogService root.

2. Add the MongoDBSettings section (in bold below):

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"

Julio Casal 7/12/21 dotnetmicroservices.com


}
},
"MongoDbSettings": {
"Host": "localhost",
"Port": "27017"
},
"AllowedHosts": "*"
}

Notice that credentials to access MongoDB have not been specified in the settings. Connecting via just
host and port works by default with MongoDB, and it should be OK for this quick guide, but you could
later improve this to also require credentials for more secure access to MongoDB.

Add a class to represent MongoDbSettings


The best way to read configuration settings into your C# code is by having a class that represents the
settings.

To add a MongoDbSettings class:

1. Create a Settings directory at the CatalogService root

2. Create a MongoDbSettings.cs file in the new Settings directory

3. Add this code to the new file:

namespace CatalogService.Settings
{
public class MongoDbSettings
{
public string Host { get; init; }

public int Port { get; init; }

public string ConnectionString => $"mongodb://{Host}:{Port}";


}
}

In the code above notice that the MongoDB connection string is being calculated via string interpolation
out of the Host and Port properties.

Register the MongoDB and repository dependencies


It’s time to tell our microservice how to create the ICatalogItemRepository instance used by our
CatalogItemsController and the IMongoDatabase instance used by CatalogItemRepository. For this we
need to perform the proper registration during app startup.

To register the MongoDB and repository dependencies:

1. Open the Startup.cs file found at the CatalogService root.

Julio Casal 7/12/21 dotnetmicroservices.com


2. Add the code in bold below to the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)


{
services.AddSingleton(serviceProvider =>
{
var mongoDbSettings = Configuration
.GetSection(nameof(MongoDbSettings))
.Get<MongoDbSettings>();
var mongoClient = new MongoClient(mongoDbSettings.ConnectionString);
return mongoClient.GetDatabase("Catalog");
})
.AddSingleton<ICatalogItemRepository, CatalogItemRepository>();

services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "CatalogService",
Version = "v1"
});
});
}

Notice the following from the code above:

• The MongoDBSettings in appsettings.json are being read via the Configuration property and the
MongoDbSettings class into the mongoDbSettings variable.

• A MongoClient object is created using MongoDbSettings.ConnectionString. This is the object that


represents the connection to the MongoDB server.

• The GetDatabase method is called on the MongoClient instance to retrieve and register the
IMongoDatabase instance needed by CatalogItemRepository. Here is where we provide the name
for our database (“Catalog”). The database is created the first time our repository tries to write to it,
so you don’t have to worry about creating it manually.

• The AddSingleton method is called to register CatalogItemRepository. By doing this the ASP.NET
Core runtime knows that any time a class (like CatalogItemsController) needs an instance of a class
that implements ICatalogItemRepository, it will try to find such instance in its bag of created
instances and if it can’t find it, it will go ahead and create an instance for us. Next time such instance
is needed, the already created instance will be reused.

Prevent the Async suffix removal on controller action names


By default the ASP.NET Core runtime will remove the Async suffix from your controller methods, like
GetByIdAsync. This means that when your controller’s PostAsync method tries to invoke this line:

return CreatedAtAction(nameof(GetByIdAsync), new { id = item.Id }, item);

Julio Casal 7/12/21 dotnetmicroservices.com


The call will fail, because GetByIdAsync would have been transformed to just GetById. Luckily, this is
easy to address with a small modification to our Startup class.

To prevent the Async suffix removal on controller action names:

1. Open Startup.cs.

2. Modify this call inside the ConfigureServices method:

services.AddControllers();

To read like this:

services.AddControllers(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
});

Build the project (again)


Just like we did before, open the terminal and build the project again to ensure everything is building
properly:

dotnet build

Start the MongoDB server


Our microservice needs a MongoDB server to store and query for catalog items. Fortunately, we don’t
need to install one in our box. We can use docker to get us a server quick.

To stand up the MongoDB server:

1. Open a terminal in VS Code

2. Type this in the terminal:

docker run -d --rm --name mongo -p 27017:27017 -v mongodbdata:/data/db mongo

That line will do the following:

• It pulls down to your box the MongoDB docker image from Docker Hub, where most of the public
docker images live. This might take a few mins depending on your internet connection the very first
time. Next time it will usually be instantaneous.

• Starts a MongoDB docker container with:

Julio Casal 7/12/21 dotnetmicroservices.com


▪ -d: Detached mode, so your terminal is free to run other commands as opposed to stay
connected to the MongoDB container
▪ -rm: Removes the docker container from your box once it is stopped
▪ --name mongo: Gives a meaningful name to the container. Otherwise, you get a random name.
▪ -p 27017:27017: Opens port 27017 in your computer so your microservice can talk to it. It also
maps that port to the same port inside the container, since that is the port that the MongoDB
server running inside the container is listening to. It’s a simple external port to internal port
mapping. We happen to be using the same port externally and internally.
▪ -v mongodbdata:/data/db: Creates a volume (storage space) in your computer so that any time
MongoDB writes to /data/db inside the container, the data is written outside the container, in a
location called mongodbdata. If you don’t do this all the data in the database is gone once you
stop the container.
▪ mongo: The final parameter is just the name of the container image to run.

Run your microservice


Now that you have MongoDB docker container up and running, it’s time to test your new microservice.
There are a few ways you can start the web server and your microservice along with it. Here for a couple
of ways:

1. From the VS Code terminal. Just type this in your terminal:

dotnet run

That should produce an output like this:

Building...
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\Repos\CatalogService

Notice the address of your web server near the top (http://localhost:5000).

2. From the Run View. You can access this view by clicking on the Run icon on the Activity bar:

After clicking on it click on Start Debugging on the top-left or just hit F5:

Julio Casal 7/12/21 dotnetmicroservices.com


The two differences with this second approach are that your web browser will automatically open
and navigate to the address of your web server (http://localhost:5000) and a debugger will get auto
attached to the web server process so you can place break points anywhere in your classes to debug
your code as you try out your microservice operations.

Explore the microservice REST API


Your microservice comes with built in Swagger UI integration. Therefore, you can start exercising its
REST API operations with no additional tooling.

To explore the microservice REST API:

1. In your browser navigate to this URL:

http://localhost:5000/swagger

2. Click on the different sections in the Swagger UI page to explore the description of each method and
the schemas of your DTOs:

Julio Casal 7/12/21 dotnetmicroservices.com


Create an item in the catalog
You can now use the POST operation on Swagger UI to create a new item in your microservice.

To create a new catalog item:

1. Click on the POST operation to expand it.

2. Click on the Try it out button.

3. Fill in a name, description and price for your new catalog item and then click on the Execute button.

Julio Casal 7/12/21 dotnetmicroservices.com


4. Scroll down a bit and you should see this:

5. Notice the following in the above image:

Julio Casal 7/12/21 dotnetmicroservices.com


• Request URL. Shows the full route where the catalog item was posted.

• Code. We got 201 (Created) meaning the catalog item was successfully created.

• Response body. Shows the full body of the created item. Notice this includes an auto generated
ID and the CreatedDate.

• Response headers. Notice the location header. It shows the route where the just created item
can be found.

Query for catalog items


You can now use the GET operations to query for the items created in the microservice’s database.

To query for catalog items:

1. Collapse the POST operation and expand the GET /items/{id} operation

2. Click on the Try it out button, enter the id of the item you created in the previous section and hit
the Execute button.

3. Scroll down a bit and you will see all the details of the catalog item in the Response body section:

Julio Casal 7/12/21 dotnetmicroservices.com


4. Try also creating another item via POST and then using the GET /items operation to retrieve all
items in the microservice’s database:

Julio Casal 7/12/21 dotnetmicroservices.com


That’s it!
You now have fully working .NET 5 microservice with a functional REST API and its own MongoDB
database. Of course, this is only the start, but I hope it gave you enough to get started and hopefully to
get excited about using the .NET platform to build your next microservice.

If you would like to follow a step by step video version of what you found in this guide, please check out
my Building Your First Microservice With .NET online course, where I also cover several other topics like:

• What is a monolith and the typical issues it presents over time


• What are microservices, the problems they solve and when to use them
• A quick view of the building blocks of a .NET microservices based architecture
• How to create a microservice from scratch using .NET 5 and Visual Studio Code
• How to implement the Update and Delete REST API operations in your microservice
• What is Docker and how it allows you to run a database server in your box
• What is dependency injection and how it works in ASP.NET Core apps
• What is and how the ASP.NET Core configuration system works

Thanks for reading! Feel free to email me any questions you might have to
julioc@dotnetmicroservices.com and I’ll try my best to get back to you or perhaps create a video to
answer your question. Please let me know if you think something is missing from this guide.

Best of luck,

Julio

Julio Casal 7/12/21 dotnetmicroservices.com

You might also like