Microservice development guide using ASP.NET Core 3.1

Microservices using ASP.NET Core 3.1 – the ultimate detailed guide

https://procodeguide.com/programming/microservices-asp-net-core/

ASP.NET Core microservice is an architecture in which applications are created as multiple small independent serviceable components. This article details how to create microservices using ASP.NET Core, Serilog, Swagger UI, health check, and Docker container.

catalogue

Microservice architecture

Monomer vs microservice

Why use ASP.NET Core microservices

Implementing microservices using ASP.NET Core

Creating services using CRUD operations

Create an ASP.NET Core project

Realize order service

Add Web API version control

Web API versioning using URL s

Add log to microservice

Logging using Serilog

Add service monitoring mechanism

Implementing microservice monitoring using ASP.NET Core Healthchecks

Creating documents for microservices using ASP.NET Core

Implementing documents using Swashbuckle Swagger

Adding containerization to microservices

Container using Docker

Benefits of microservices

Disadvantages of microservices

Best practices

generalization

Source code download

Microservice architecture

The microservice architecture style is shown in the figure above. Microservice architecture is a style of developing a large application into a group of small independent services. Here, each service implements specific functions and has its own data storage. Each service function should be small enough to implement a use case and large enough to provide some value. Each service should be deployed separately so that it can scale independently. These services should be independent of each other as much as possible. If Inter service communication is required, some lightweight communication protocols can be used.

Identity providers are used to provide user authentication services to applications.

To learn more about identity providers and how to secure ASP.NET Core based applications, you can see my about* ASP.NET Core security *Series

The API gateway is a single entry point for all requests, helping to manage endpoints and coordinate different services.

A container is a standard software unit that bundles applications or functions and all their dependencies so that applications can be deployed quickly and reliably on any new system with a container host.

Container orchestration is software used to manage the container lifecycle in large applications, which helps to scale applications according to load.

Microservices are actually independent small services bundled with its dependencies in the container

Data Store is used to store micro service data. The basic principle is that each service manages its own data.

Monomer vs microservice

Integral type Microservices
A single service / application should contain all business functions A single service should contain only one business function
All services are tightly coupled All services are loosely coupled
Applications are developed in a single programming language Each service can use a different programming language
A single database for all services. Each service has a separate database
All services need to be deployed on the VM together Each service can be deployed on a separate virtual machine
All services run in the same process, so if a service fails, the entire application will be interrupted Each service runs in a different process, so the failure of one service will not affect other services
It is difficult to extend a particular service because the new instance must have all the services It can be easily expanded because any single service can be deployed independently
A single large team handles the entire application The independent small team of each service works more intensively.
Easy to develop and test small applications Because it is a distributed system, it increases the complexity of the application

Why use ASP.NET Core microservices?

. NET Core provides the following advantages for microservices using ASP.NET Core

  • Lightweight framework built from scratch
  • Cross platform support
  • Optimized for containerization

Implementing microservices using ASP.NET Core

This is a short video about implementing microservices using ASP.NET Core (. NET Core microservice example). Here, we will introduce in detail the step-by-step process of creating microservices using ASP.NET Core. We will create an order service that will provide endpoints to add, cancel, get orders by ID, and get orders by customer ID in the application. This presentation has been executed in Visual Studio 2019 version 16.6.2

Creating services using CRUD operations

Create an ASP.NET Core project

For microservices with ASP.NET Core demo, we will create an ASP.NET Core 3.1 Web API project to demonstrate. net Core Web API microservices.

Realize order service

In order to demonstrate microservices using ASP.NET Core, we will create an order microservice, which will contain functions related only to orders. This is the basic requirement of microservices. It should only implement one function, so our microservices will only include order related functions. We will implement the following interface functions

  • Add – create a new order
  • Cancel – cancel an existing order
  • GetById – get order by ID
  • GetByCustomerId – gets all orders for the customer ID

Next, we will quickly add an entity model to the order and enable the Entity Framework core for the order microservice.

If you need more details on how entity frameworks work, please see my about* Of Entity Framework Core in ASP.NET Core 3.1 *Another article

Add model

Add order entity class

public class Order
{
    public string Id { get; set; }
    public string ProductId { get; set; }
    public double Cost { get; set; }
    public DateTime Placed { get; set; }
    public string CustomerId { get; set; }
    public string Status { get; set; }
}
Add CRUD operations to the API using the Entity Framework core

We will use the Entity Framework Core to implement the database operation of order service.

Install the required packages for the physical framework core
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Add database context class

This is the main class coordinated with the Entity Framework function of a given model class.

public interface IApplicationDbContext
{
    DbSet<Order> Orders { get; set; }
    Task<int> SaveChanges();
}
public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
    {
    }
    public DbSet<Order> Orders { get; set; }
    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}
Add order warehouse

A repository is a component that encapsulates objects related to data stores and operations performed on them. DbContext uses dependency injection as a parameter in the constructor.

public interface IOrderRepository
{
    Task<string> Add(Order order);
    Task<Order> GetById(string id);
    Task<string> Cancel(string id);
    Task<Order> GetByCustomerId(string custid);
}
public class OrderRepository : IOrderRepository
{
    private IApplicationDbContext _dbcontext;
    public OrderRepository(IApplicationDbContext dbcontext)
    {
        _dbcontext = dbcontext;
    }

    public async Task<string> Add(Order order)
    {
        _dbcontext.Orders.Add(order);
        await _dbcontext.SaveChanges();
        return order.Id;
    }

    public async Task<string> Cancel(string id)
    {
        var orderupt = await _dbcontext.Orders.Where(orderdet => orderdet.Id == id).FirstOrDefaultAsync();
        if (orderupt == null) return "Order does not exists";

        orderupt.Status = "Cancelled";

        await _dbcontext.SaveChanges();
        return "Order Cancelled Successfully";
    }

    public async Task<Order> GetByCustomerId(string custid)
    {
        var order = await _dbcontext.Orders.Where(orderdet => orderdet.CustomerId == custid).FirstOrDefaultAsync();
        return order;
    }

    public async Task<Order> GetById(string id)
    {
        var order = await _dbcontext.Orders.Where(orderdet => orderdet.Id == id).FirstOrDefaultAsync();
        return order;
    }
}
Connect application to database

Specify the SQL Server connection string in the appsettings.json file.

   "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=OrderDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
Register the service in the startup class

You need to configure the database context and order repository as services in the ConfigureServices method in the startup class

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
                    options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection"),
                    ef => ef.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName)));
    services.AddScoped<IApplicationDbContext>(provider => provider.GetService<ApplicationDbContext>());

    services.AddTransient<IOrderRepository, OrderRepository>(); services.AddControllers();

//Remaining code has been removed
}
Add migration

To automate the migration and create the database, we need to run the following command in the package manager console.

add-migration InitialMigration
update-database

One thing to note here is that this table is only created for order details. The product and customer tables are not created using foreign key references, because you must keep the microservice small and focus on a single function. Products and customers will be in their own separate database and have their own microservice implementation. If you need to display the product details or customer details as part of the order details, you need to call the corresponding microservice and get the required details.

Add order controller

This is the code for the order controller, which has been added to expose the endpoint of the order microservice. The order repository has passed dependency injection as a constructor parameter

[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
    private IOrderRepository _orderRepository;
    public OrderController(IOrderRepository orderRepository)
    {
        _orderRepository = orderRepository;
    }

    [HttpPost]
    [Route("Add")]
    public async Task<ActionResult> Add([FromBody] Order orderdet)
    {
        string orderid = await _orderRepository.Add(orderdet);
        return Ok(orderid);
    }

    [HttpGet]
    [Route("GetByCustomerId/{id}")]
    public async Task<ActionResult> GetByCustomerId(string id)
    {
        var orders = await _orderRepository.GetByCustomerId(id);
        return Ok(orders);
    }

    [HttpGet]
    [Route("GetById/{id}")]
    public async Task<ActionResult> GetById(string id)
    {
        var orderdet = await _orderRepository.GetById(id);
        return Ok(orderdet);
    }

    [HttpDelete]
    [Route("Cancel/{id}")]
    public async Task<IActionResult> Cancel(string id)
    {
        string resp = await _orderRepository.Cancel(id);
        return Ok(resp);
    }
}

Our order service is ready to perform the operation. However, to make it a microservice, we must enable logging, exception handling, documentation, monitoring, containerization and other functions. Let's look at how to use ASP.NET Core to implement these functions for microservices

Add Web API version control

Microservices should be built in such a way that they are easy to change without destroying existing clients, and should also be able to support multiple versions in parallel, so Web API version control will help us achieve this.

Microservices with ASP.NET Core support Web API version control. Using this function, we can implement multiple versions of the same API so that different clients can use the required version of API.

Web API versioning using URL s

In Web API version control using URLs, the version number is part of the URL, that is http://server:port/api/v1/order/add

Install Web API version control package
Install-Package Microsoft.AspNetCore.Mvc.Versioning
Configure Web API versioning in the startup class

Enable support for Web API versioning in the ConfigureServices method of the startup.cs file.

public void ConfigureServices(IServiceCollection services){    services.AddApiVersioning(apiVerConfig =>    {        apiVerConfig.AssumeDefaultVersionWhenUnspecified = true;        apiVerConfig.DefaultApiVersion = new ApiVersion(new DateTime(2020, 6, 6));    });    //Remaining code has been removed }
Add URL Web API version control to order controller

You need to add a parameter v{version:apiVersion} such as R out ("api/v{version:apiVersion}/[controller]") to the routing attribute so that the API version becomes a part of the URL.

[ApiVersion("1.0")][Route("api/v{version:apiVersion}/[controller]")][ApiController]public class OrderController : ControllerBase{//Remaining code has been removed}

If you need more details about Web API versioning in ASP.NET Core, please see my about* Web API version control in ASP.NET Core 3.1 *Another article

Add log to microservice

Once deployed to production, it should be easy to track and analyze problems. Logs help us analyze complex problems that may sometimes be difficult to simulate. There is always a need to troubleshoot application problems that require log analysis. For microservices with ASP.NET Core, we will use Serilog to record the details of the application.

Logging using Serilog

There are many third-party components, one of which is* Serilog *. Serilog is a popular third-party logging provider, which is supported in ASP.NET Core logging.

For a detailed explanation of implementing Serilog in ASP.NET Core, please refer to another article of mine* ASP.NET Core Logging with Serilog*

Install the required Serilog package
Install-Package Serilog.AspNetCoreInstall-Package Serilog.Extensions.LoggingInstall-Package Serilog.Extensions.HostingInstall-Package Serilog.Sinks.RollingFileInstall-Package Serilog.Sinks.Async
Add Serilog configuration

Serilog RollingFile Sink is implemented by adding configuration in appsettings.json file. You can specify the log level by setting the log level value in the property named MinimumLevel.

 "Serilog": {    "MinimumLevel": "Information",    "WriteTo": [      {        "Name": "Async",        "Args": {          "configure": [            {              "Name": "RollingFile",              "Args": {                "pathFormat": "Serilogs\\AppLogs-{Date}.log",                "outputTemplate": "{Timestamp:HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}",                "fileSizeLimitBytes": 10485760              }            }          ]        }      }    ]  }
Configure Serilog in the program & startup class

Add UseSerilog() to CreateDefaultBuilder in Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>    Host.CreateDefaultBuilder(args)        .UseSerilog()        .ConfigureWebHostDefaults(webBuilder =>        {            webBuilder.UseStartup<Startup>();        });

Load the Serilog configuration from the appsettings.json file in Startup.cs

public Startup(IConfiguration configuration){    Configuration = configuration;    Log.Logger = new LoggerConfiguration()        .ReadFrom.Configuration(configuration)        .CreateLogger();}
Add logging in order controller

The Serilog.Log class has been used to add a log with a Debug method for debugging and errors in the event of some exceptions.

[HttpPost][Route("Add")]public async Task<ActionResult> Add([FromBody] Order orderdet){    try    {        Log.Debug("Order Addition Started");        Log.Debug("Order Addition Input", orderdet);        string orderid = await _orderRepository.Add(orderdet);        Log.Debug("Order Addition Output", orderid);        return Ok(orderid);    }    catch (Exception ex)    {        Log.Error("Order Addition Failed", ex);        throw new Exception("Order Addition Failed", innerException: ex);    }}//Remaining code has been removed

For demonstration purposes, I only added logging in the controller operation "add", but as a good practice, you need to add logs in a complete application with the appropriate log level, which helps debug complex problems in production. Since this is a microservice, asynchronous log writing has been configured to reduce the overhead of logging calls by delegating work to background threads.

In addition, another thing to note here is that if you run the ASP.NET core application in the docker container, you need to be careful about the location of the log file, just as if you store the log file in the same container, you may lose that data. In a containerized environment, logs should be stored on a persistent volume.

Add health check mechanism

It is always good to check that our service is up and running or functioning properly. Before our customers inform us of our damaged services, we should be able to proactively identify our damaged services and take corrective measures. Healthchecks allows us to check whether the service is healthy, that is, start and run. The Healthcheck endpoint can also be used to check its status from the load balancer. If our service returns that the health check on the server fails, the server on the load balancer will be disabled. For microservices with ASP.NET Core, we will use ASP.NET Core HealthChecks for monitoring.

Implementing microservice monitoring using ASP.NET Core Healthchecks

Healthchecks is a built-in Middleware in ASP.NET Core to report the health of applications. The health check can be exposed as another endpoint in the application.

Install HealthChecks package

Use the package manager console to install packages required for health checks

Install-Package Microsoft.Extensions.Diagnostics.HealthChecksInstall-Package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
Configure health checks in the Startup class

Here, we configure the basic application health check and Entity Framework database context health check to confirm that the application can communicate with the database configured for the Entity Framework core DbContext

public class Startup{    public void ConfigureServices(IServiceCollection services)    {        services.AddHealthChecks()        .AddDbContextCheck<ApplicationDbContext>();    }    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    {        app.UseHealthChecks("/checkhealth");   }    //Remaining code has been removed}

You can use the URL http://serverip : port/checkhealth check service health

Creating documents for microservices using ASP.NET Core

It's always good to maintain updated documents for microservices. Other teams should be able to refer to these API specifications and use microservices accordingly. We will implement Swashbuckle.AspNetCore to generate Swagger documents for order microservices.

Implementing documents using Swashbuckle Swagger

Swashbuckle is an open source library for generating swagger documents for asp.net core web APIs. This document can be used to explore and test APIs.

Install the required swashbuckle swagger package

Install the packages required for swashbuckle using the package manager console

Install-Package Swashbuckle.AspNetCoreInstall-Package Microsoft.OpenApi
Configure swagger startup class
public class Startup{    public void ConfigureServices(IServiceCollection services)    {        services.AddSwaggerGen(options =>        {            options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo            {                Title = "Microservice - Order Web API",                Version = "v1",                Description = "Sample microservice for order",            });        });    }    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    {        app.UseSwagger();        app.UseSwaggerUI(options => options.SwaggerEndpoint("/swagger/v1/swagger.json", "PlaceInfo Services"));    }    //Remaining code has been removed}

The following is the document generated for the order micro service using swagger

Adding containerization to microservices

Containerization is used to bundle the functionality of an application or application, all dependencies, and their configuration in the container image. This image is deployed on the host operating system, and the bundled application works as a unit. The concept of container images allows us to deploy these images across environments with little modification. In this way, microservices can be easily and quickly extended because new containers can be easily deployed for short-term purposes.

Docker will be used to add containerization to our microservices. Docker is an open source project for creating containers that can run on cloud or local docker hosts.

Container using Docker

This is a quick. NET core microservice docker tutorial. To enable docker support in ASP.NET Core projects on projects in solution explorer, select Add = > docker support from the menu

This will enable docker and create a Dockerfile for the project, as shown below

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

#Depending on the operating system of the host machines(s) that will build or run the containers, the image specified in the FROM statement may need to be changed.
#For more information, please see https://aka.ms/containercompat

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-nanoserver-sac2016 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-nanoserver-sac2016 AS build
WORKDIR /src
COPY ["ProCodeGuide.Sample.Microservice/ProCodeGuide.Sample.Microservice.csproj", "ProCodeGuide.Sample.Microservice/"]
RUN dotnet restore "ProCodeGuide.Sample.Microservice/ProCodeGuide.Sample.Microservice.csproj"
COPY . .
WORKDIR "/src/ProCodeGuide.Sample.Microservice"
RUN dotnet build "ProCodeGuide.Sample.Microservice.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "ProCodeGuide.Sample.Microservice.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ProCodeGuide.Sample.Microservice.dll"]

This allows the application to run in a container on the Docker host. To do this, you can install on a Windows machine* docker desktop*.

Benefits of microservices

  • Gentle learning curve - when business functions are broken down into small services, it allows developers to learn only the functions of the service.
  • Better scaling - each service is deployed independently, so it can be scaled separately.
  • Shorter development time - the application development cycle can be shorter because each service component is independent of each other.
  • Fault isolation - the failure of one service does not affect the operation of other services.
  • Dependencies are simpler -- not dependent on a single programming language and deployment model.
  • Because different teams are developing different services, different services can be developed in parallel and quickly.

Disadvantages of microservices

  • Small independent services need to coordinate with each other, which may not be simple compared with single applications
  • Managing distributed transactions across multiple services can be complex.
  • There are multiple services / components to monitor.
  • Since each independent service needs to be tested before integration testing, testing may take little time.
  • The entire deployment architecture for large applications becomes very difficult to manage.

Microservice architecture is about better handling large complex systems, but to achieve this, it will expose a series of complexity and implementation challenges. Despite the shortcomings or problems, the benefits of adopting microservices are the driving factor for many companies to implement microservices.

Best practices

  • Microservices should implement only a single function that should deliver value.
  • Each microservice should have its own data store, which should not be shared between services.
  • Always maintain updated documents for microservices.
  • Use containers to deploy services.
  • DevOps and CI/CD practices

generalization

We introduced what is the microservice architecture and how to start using the microservices of ASP.NET Core 3.1. I haven't introduced a more important function of microservices, namely automated testing. Automated unit testing itself is a huge topic, and I will write a separate article to discuss it.

Quick review of. Net Core with microservice features

  • A microservice is an architecture in which an application is created as a number of small, independent serviceable components
  • Microservices should contain only a single business function and should be small enough to remain focused and large enough to deliver value.
  • Each microservice should have its own data store.
  • Microservices can communicate with each other using lightweight protocols, that is, through HTTP or advanced message queuing protocol (AMQP)
  • Microservices can be deployed independently on a separate virtual machine and can be expanded independently.
  • Implementing API gateways for large and complex microservice based applications has many benefits.

Posted on Sat, 30 Oct 2021 10:03:48 -0400 by everurssantosh