Select Page
Microservices with ASP.NET Core 3.1

Microservices with ASP.NET Core 3.1

Microservices is a type of Architecture in which application is created as multiple small independent serviceable components. This article will cover in detail how to create microservices with ASP.NET Core, Serilog, Swagger UI, Health Checks & Docker containers.

Microservices Architecture

Microservices with ASP.NET Core

The Microservices architecture style is shown in the figure above. Microservices Architecture is a style in which one large application is developed as a set of small independent services. Here each service implements a specific functionality and has its own data store. Each service functionality should be small enough to implement just one use case and big enough to provide some value. Each service should be deployable separately so that it can be scaled independently. As far as possible these services should be independent of each other and if there is a need for inter-service communication then some lightweight communication protocol can be used.

Identity Provider is used to provide user authentication services to an application.

To know details about Identity Provider & also to know about how to secure your ASP.NET Core based application you can check my series on ASP.NET Core Security

API Gateway is a single entry point for all requests that help in managing the endpoints and coordinates with different services.

Container is a standard unit of software that bundles application or feature and all of its dependencies so that application can be deployed quickly and reliably on any new system that has container host.

Container Orchestration is a piece of software that is used to manage the life-cycle of containers in a large application. This helps to scale application on basis of the load.

Microservice is the actual small independent service which is bundled in a container along with it dependencies

Data Store is used to store microservice data and the basic principle is that each service manages its own data.

Monolithic v/s Microservices

MonolithicMicroservices
Single service/application should contain all the business functionalitySingle service should contains only one business functionality
All service are tightly coupledAll services are loosely coupled
Application is developed in one single programming languageEach service can be in different programming language
Single database for all services.Each service has separate database
All services needs to be deployed together on VMEach service can be deployed on separate VM
All services run in same process so if one service goes down then whole application breaksEach service runs in different process so failure of one service does not affects other services
Difficult to scale a particular service as new instance will have to have all servicesCan be Scaled easily as any single service can be deployed independently
Single large team works on whole applicationSeparate small team work on each Service which are more focused.
Simple to develop & test small applicationsAdd complexity to the application by the fact that its a distributed system

Here is a one of the good books on Rest API with ASP.NET Core

Why microservices with ASP.NET Core?

.NET Core provides following advantages which works for microservices

  • A light-weight framework built from ground up
  • Cross-platform support
  • Optimized for containerization

Implement Microservices with ASP.NET Core

Here we will cover in detail the step by step process to create microservice with ASP.NET Core. We will be creating an order service that will provide endpoints to Add, Cancel, Get Order By Id & Get Order(s) By Customer Id in the application. This demo has been executed in Visual Studio 2019 version 16.6.2

Create Service with CRUD operations

Create ASP.NET Core Project

For microservices demo we will be creating a ASP.NET Core 3.1 Web API project.

ASP.NET Core 3.1 API Project Creation

Implement Order Service

We will be creating order microservice which will contain functionality only related to orders. We will be implementing following endpoints

  • Add – To create a new order
  • Cancel – To cancel an existing order
  • GetById – Get Order by Id
  • GetByCustomerId – Get all orders for Customer Id

Below we will quickly add the entity model for Order & enable entity framework core for order microservice.

If you need further details on how an entity framework works then check my other article on Entity Framework Core in ASP.NET Core 3.1

Add Model

Add an 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 with entity framework core

We will make use of Entity Framework Core to implement database operations for the order service.

Install required packages for entity 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 that co-ordinates with entity framework functionality for 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 Repository

The repository is a component that encapsulates the objects related to data storage and operations performed over them. DbContext is passed as a parameter in the constructor using dependency injection.

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 the database

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

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

You need to configure the database context & order repository as a service in method ConfigureServices 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 Migrations

To automate the migrations & create a database we need to run the following commands in the package manager console.

add-migration InitialMigration
update-database
EF Core Migrations
EF Core Generated Database

One thing to note here is that table is created for order details only. Product & Customer tables are not created with foreign key reference as you have to keep microservice small & focussed on one single functionality. Product & Customer will be in a separate database of their own with their own microservice implementation. If you need product details or customer details to be displayed as part of order details then you need to call respective microservice and fetch required details.

Add Order Controller

Here is the code for the order controller which has been added to expose the endpoints of the order microservice. Order Repository has been passed as a constructor parameter using dependency injection

[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 operations. But to make it a microservice we will have to enable features like Logging, Exception Handling, Documentation, Monitoring, Containerization, etc.

Add Web API versioning

Microservices should be easy to change without breaking existing clients and also should be able to support multiple versions side by side so Web API versioning will help us achieve this.

Web API versioning is a feature using which we can implement multiple versions of the same API so that different clients can work with the required version of API.

Implement Web API versioning using URL

In Web API versioning using URL, the version number is part of the URL i.e. http://server:port/api/v1/order/add

Install Web API versioning package
Install-Package Microsoft.AspNetCore.Mvc.Versioning
Configure Web API versioning in Startup class

Enable support for Web API versioning in ConfigureServices method in 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 versioning to order controller

You need to add parameter v{version:apiVersion} in route attribute like Route(“api/v{version:apiVersion}/[controller]”) so that API version becomes part of URL.

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

If you need further details on Web API versioning in ASP.NET Core then check my other article on Web API Versioning in ASP.NET Core 3.1

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Add logging to microservice

After deploying to production it should be easy to track and analyze issues. Logs help us to analyze complex issues which sometimes might be difficult to simulate. There will always be a need to troubleshoot application issues for which logs will be required for analysis.

Implement logging with Serilog

There are many third-party providers and one of these is Serilog. Serilog is a popular third party logging provider that is supported in ASP.NET Core Logging.

Install required Serilog packages
Install-Package Serilog.AspNetCore
Install-Package Serilog.Extensions.Logging
Install-Package Serilog.Extensions.Hosting
Install-Package Serilog.Sinks.RollingFile
Install-Package Serilog.Sinks.Async
Add Serilog configuration

Serilog RollingFile Sink is implemented by adding configuration to the appsettings.json file. Log level can be specified 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 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 Serilog configuration from 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

Serilog.Log class has been used to add logs with method Debug for debugging & Error in case of some exception.

[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

Here for demonstration purposes, I have added logging feature only to the controller action ‘Add’ but as good practice, you need to add logs in a complete application with proper log levels which can assist in debugging complex issues on production. Since this is microservice, Async log writing has been configured as that reduces the overhead of logging calls by delegating work to a background thread.

Also, one more thing to note here is if you are running ASP.NET core application in docker container then you need to be careful with log file location as if you store the log file in the same container itself then there is a possibility of losing that data. In containerized environment logs should be stored on some persistent volume.

If you need further details on Logging with Serilog in ASP.NET Core then check my other article on ASP.NET Core Logging with Serilog

Add service monitoring mechanism

It is always good to keep a check on whether our service is up and running or functioning properly. Before our clients inform us about our broken service we should be able to proactively identify our broken services and take corrective actions. Healthchecks allow us to check if service is healthy i.e. up & running. Healthcheck endpoint can also be used to check its status from loadbalancer & disable a server on load balancer if our service returned failure for healthcheck on that server.

Implement microservice monitoring using ASP.NET Core Healthchecks

Healthchecks is an in-built middleware in ASP.NET Core for reporting the health of an application. Healthchecks can be exposed as one more endpoint in the application.

Install healthchecks packages

Using package manager console install the required packages for healthchecks

Install-Package Microsoft.Extensions.Diagnostics.HealthChecks
Install-Package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore
Configure healthchecks ins Startup class

Here we have configured basic application health check & Entity Framework database context health check that confirms that the app can communicate with the database configured for an 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 check the service health using URL http://serverip:port/checkhealth

Create documentation for microservice

It is always good to maintain updated documentation for microservices. Other teams should be able to refer to these API specifications and consume microservice accordingly. We will be implementing Swashbuckle.AspNetCore for generating Swagger documents for order microservice.

Implement documentation using Swashbuckle Swagger

Swashbuckle is an open-source library to generate swagger documentation for ASP.NET Core Web API. This documentation can be used to explore and test API.

Install required swashbuckle swagger packages

Using package manager console install the required packages for swashbuckle

Install-Package Swashbuckle.AspNetCore
Install-Package Microsoft.OpenApi
Configure swagger ins 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
}

Below is the documentation generated with swagger for order microservice

ASP.NET Core Swagger

Add containerization to microservice

Containerization is used to bundle an application or feature of an application, all of it dependencies & its configuration in a container image. This image is deployed on the host operating system and bundled application works as a unit. This concept of container images allows us to deploy these across environments with little or no modifications at all. This way it is easy to scale out microservice quickly as the new containers can be easily deployed for short term purposes.

Docker will be used to add containerization to our microservice. Docker is an open-source project for creating containers that can run on docker host either on cloud or on-premises.

Implement containerization using Docker

To enable docker support in ASP.NET Core project right on the project in the solution explorer and select Add=>Docker Support from the Menu

Enable Docker in ASP.NET Core

This will enable docker and also create a Dockerfile to 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 makes the application run within a container on the Docker host. For this to work docker desktop should be installed on the windows machine.

Benefits of Microservices

  • Gentle Learning Curve – As business functionality is broken into small services it allows developers to learn just the functionality of that service only.
  • Better Scaling – Each service is deployed independently and so can be scaled separately.
  • Lesser time to market – Application development cycle can be shorter as each service component is independent of each other.
  • Fault Isolation – Failure of one service does not affect the operation of other services.
  • No dependency on a single programming language & deployment model.
  • Parallel & Fast development of different Services is possible as separate teams are working on different services.

Drawbacks with Microservices

  • Small independent services require coordination among each other which may be not simple as compared to Monolith Application
  • Managing distributed transactions across multiple services can be complex.
  • There are Multiple Services/Components to Monitor.
  • Testing can be little time consuming as each independent service needs to be tested before integrated testing.
  • Whole deployment architecture for large applications becomes very Complex to Manage.

Microservices Architecture is about better handling a large & complex system but to achieve that it exposes its own set of complexities & implementation Challenges. Despite the disadvantages or problems, the benefits of adopting Microservices are driving factors for many companies to implement Microservices.

Best Practices

  • Microservice should implement only one single functionality that should be able to deliver value.
  • Each Microservice should have their own datastore and this should not be shared across services.
  • Always maintain updated documentation for Microservices.
  • Use containers for the deployment of services.
  • DevOps & CI/CD practices

Summary

We covered what is microservice architecture and how to get started with microservices with ASP.NET Core 3.1. I have not covered one more important feature of microservice i.e automated testing. Automated unit testing is a very vast topic in itself and I will do a separate article on it.

Quick recap on microservices characteristics

  • Microservices is a type of Architecture in which application is created as multiple small independent serviceable components
  • Microservice should contain only single business functionality and should be small enough to stay focussed and big enough to deliver value.
  • Each Microservice should have its own data store.
  • Microservices can communicate with each other using lightweight protocol i.e. over HTTP or Advanced Message Queue Protocol (AMQP)
  • Microservice can be deployed independently on a separate VM and can be scaled independently.
  • There are benefits in implementing API Gateway for large & Complex Microservices based Applications.

Source code download

Entity Framework Core in ASP.NET Core 3.1

Entity Framework Core in ASP.NET Core 3.1

This article will be covering in detail the implementation of Entity Framework Core in ASP.NET Core Web API. We will learn about entity framework core in details i.e. what, how & why and see how to use it in ASP.NET Core Web API to implement CRUD operations. Though here we will be using entity framework in ASP.NET Core this can be implemented even in .NET Framework for your data persistence. I hope you will join me on this and go through step by step demonstration on implementing entity framework core in ASP.NET Core Web API.

Introduction to Entity Framework Core

ASP.NET Core Entity Framework

Entity Framework has been around since 2008 which has improved over the years. Entity framework core was introduced for .NET Core based applications. It is an open-source object-relational mapper (ORM) built over ADO.NET and an official data access platform from Microsoft. Entity framework core has been rewritten from scratch to make it lightweight, flexible & achieve better performance.

In the absence of an ORM typically lots of code have to be written to perform CRUD operations with the database. using an ORM like entity framework can reduce the number of lines of code to be written for database interactions. Typical ORM will work on classes & database tables of similar structure but entity framework core implements mappings which makes it a lot more flexible i.e. in case table & class does not match then mappings can be exercised to get from objects to tables.

Entity framework core can work with wide variety of databases. Microsoft provides rich providers for SQL Server & SQLite and there are many other third party providers some commercial and some from community.

How does entity framework core works?

How Entity Framework Works
  1. Define domain classes & configure entity framework DbContext API along with mappings to let entity framework work how this class maps to the database schema.
  2. Write LINQ to objects to fetch data from the database.
  3. Based on mappings & database provider configured entity framework will translate queries into SQL that’s understood by the database.
  4. The query is executed and the object is populated with data.
  5. Data in objects can be modified, deleted or new records can be added to the list & once done call DbContext.saveChanges from EF API to save data back to the database.
  6. The entity framework will generate the required SQL statements to save data back to the database.

Should we use Entity Framework Core?

Entity framework can increase developer productivity but still many developers don’t prefer using it due to the complexity it adds to data access layer.

Use entity framework when

  • There is a one-to-one mapping between fields on-screen and columns in the database tables.
  • You need to implement CRUD operation for the screen and business logic is not so complex.
  • There are no bulk database operations to be implemented.
  • There is no need to bother about the SQL generated for the operation i.e. no SQL tweaking are required for the performance

Code First v/s Database First Approach

Entity Framework Database First vs Code First Approach

Entity framework provides two approaches for development

  1. Database First Approach: This is used when you already have a database ready or you have a team of DBAs and take the approach that you will be designing the database first and then the application. Entity framework will create Entities for you from the database. Manual changes to the database can be done and the model can be updated from the database.
  2. Code First Approach: This is used when there is a team of developers who need full control over the code. The entity framework will generate database tables for you based on the domain classes. Here database is just storage with no business logic in the database layer. Any manual changes to the database will be lost.

Implement Entity Framework Core in ASP.NET Core Web API

Demonstration video

Create a project for demonstration

For demonstration purposes, we will be creating a default ASP.NET Core 3.1 API Project using Visual Studio 2019 Community Edition. We will be using the code-first approach for the demonstration.

ASP.NET Core 3.1 API Project Creation

Create the data model

Create an Employee entity class. Here Id will become the primary key column for the database table that will be created for this class. Entity framework interprets property named as Id or ID as the primary key

public class Employee
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public string Designation { get; set; }
    public double Salary { get; set; }
}

Install Entity Framework Core Packages

We need to install the required entity framework packages. You run the below mentioned commands in Package Manager or install required Nuget packages from Nuget Package Manager.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools
Install Entity Framework Core

Adding database context class

This is the main class that co-ordinates entity framework functionality for a given model class. You need to derive the class from entity framework DbContext class and specify the entities included in the data model. This class creates a DbSet property for the Employee entity set. An entity set typically represents a database table and an entity represents a row in the table.

Database table will have the same name as DbSet property name.

public interface IApplicationDbContext
{
    DbSet<Employee> Employees { get; set; }
    Task<int> SaveChanges();
}
public class ApplicationDbContext : DbContext, IApplicationDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public DbSet<Employee> Employees { get; set; }

    public new async Task<int> SaveChanges()
    {
        return await base.SaveChangesAsync();
    }
}

Connect application to the database

Specify the SQL Server connection string in appsettings.json file. We are using localdb database which is light-weight version of the SQL Server Express database engine.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=EFCoreDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Register database context for dependency injection in Startup class

You need to configure the database context as service in configureservices in the startup.cs file so that you pass this service to the controller through the constructor parameter.

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.AddControllers();
}
//Remaining code has been removed

For more details on Dependency Injection, you can read my another article – https://procodeguide.com/programming/dependency-injection-in-asp-net-core-3/

Add Migrations

To automate the migrations from our entity framework class the database we need to run the command add-migration in the package manager console.

add-migration FirstMigration

This will add migration folder & required migration files to the solution as shown below. These files will be on the basis entities we configured.

Entity Framework Migrations

To create database from the migration created above we need to command update-database in the package manager console

update-database

The database as shown below will be created based on entities configured. Here it has created an Employee table based on the Employee model configured.

Entity Framework Core - Code First Database

Add CRUD operations in Repository for Employee

Repository is a component that encapsulated the objects related to data store and operations performed over them. You can even write a generic repository but for demonstration purpose I have added a repository specific to Employee entity. Interface will contain the operations that are possible (based on requirements) on the entity i.e. CRUD operations. DbContext is passed as parameter in constructor using dependency injection. Here we have added following operations.

  • Create – to add a new employee
  • GetAll – to get a list of all employees
  • GetById – to get particular employee by Id
  • Update – to modify existing employee
  • Delete – to delete existing employee
public interface IEmployeeRepository
{
    Task<int> Create(Employee employee);
    Task<List<Employee>> GetAll();
    Task<Employee> GetById(int id);
    Task<string> Update(int id, Employee employee);
    Task<string> Delete(int id);
}
public class EmployeeRepository : IEmployeeRepository
{
    private IApplicationDbContext _dbcontext;
    public EmployeeRepository(IApplicationDbContext dbcontext)
    {
        _dbcontext = dbcontext;
    }

    public async Task<int> Create(Employee employee)
    {
        _dbcontext.Employees.Add(employee);
        await _dbcontext.SaveChanges();
        return employee.Id;
    }

    public async Task<List<Employee>> GetAll()
    {
        var employees = await _dbcontext.Employees.ToListAsync<Employee>();
        return employees;
    }

    public async Task<Employee> GetById(int id)
    {
        var employee = await _dbcontext.Employees.Where(empid => empid.Id == id).FirstOrDefaultAsync();
        return employee;
    }

    public async Task<string> Update(int id, Employee employee)
    {
        var employeeupt = await _dbcontext.Employees.Where(empid => empid.Id == id).FirstOrDefaultAsync();
        if (employeeupt == null) return "Employee does not exists";

        employeeupt.Designation = employee.Designation;
        employeeupt.Salary = employee.Salary;

        await _dbcontext.SaveChanges();
        return "Employee details successfully modified";
    }

    public async Task<string> Delete(int id)
    {
        var employeedel = _dbcontext.Employees.Where(empid => empid.Id == id).FirstOrDefault();
        if (employeedel == null) return "Employee does not exists";

        _dbcontext.Employees.Remove(employeedel);
        await _dbcontext.SaveChanges();
        return "Employee details deleted modified";
    }
}

As shown below register Employee repository as a service in the ConfigureServices method of Startup class

services.AddTransient<IEmployeeRepository, EmployeeRepository>();

Repository helps in better testing of the code. Since repository is passed as service till database is ready we can create a dummy repository service for simulation of CRUD operations. Also we can pass different repository implementation based of environments. If you want to read more about how to handle multiple environment you can read my article on Configuring multiple ASP.NET Core hosting environment

Add Employee controller

Here is the code for the Employee controller which has been added to expose the CRUD operations for the Employee entity as a API. Employee Repository has been passed as constructor parameter using dependency injection. Implementation have been added by calling the methods in repository class. I have not covered validations here but before calling repository method input should be validated.

[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
    private IEmployeeRepository _employeerepository;
    public EmployeeController(IEmployeeRepository employeerepository)
    {
        _employeerepository = employeerepository;
    }

    [HttpPost]
    [Route("Create")]
    public async Task<ActionResult> create([FromBody] Employee employee)
    {
        int empid = await _employeerepository.Create(employee);
        return Ok(empid);
    }

    [HttpGet]
    [Route("GetAll")]
    public async Task<ActionResult> GetAll()
    {
        var employees = await _employeerepository.GetAll();
        return Ok(employees);
    }

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

    [HttpPut]
    [Route("Update/{id}")]
    public async Task<IActionResult> Update(int id, Employee employee)
    {
        string resp = await _employeerepository.Update(id, employee);
        return Ok(resp);
    }

    [HttpDelete]
    [Route("Delete/{id}")]
    public async Task<IActionResult> Delete(int id)
    {
        var resp = await _employeerepository.Delete(id);
        return Ok(resp);
    }
}

Let’s run & test the code

Will be using postman to test the code.

Create – request & response. Here we can see that we are sending a post HTTP request for creating along with Employee object in the body in JSON format & in response we get employee Id

EF Core - Create Request

GetAll – request & response. Here we are sending a get HTTP request without any parameters & in response we get all the employees present in database.

EF Core - GetAll Request

Entity Framework Conventions

We saw that we just added the entity class for Employee and the table was created for this entity without specifying any metadata for the database or table. This is due to the fact that the entity framework does this based on the default conventions or assumptions it follows.

  • DbSet property name is used for table name.
  • Entity class property name is used for column name.
  • Entity class properties with name ID or classnameID are assumed as primary key.

You can override these conventions by specifying your own details for the table name, column name, primary key, and foreign key details.

Modifying the database with Annotations

Entity class can be modified to specify the metadata for the table being created for entity i.e. it will override the default conventions used for migrations. Data Annotations Attributed available in namespace “System.ComponentModel.DataAnnotations” to specify the column properties for table.

public class Employee
{
    public int Id { get; set; }
        
    [Required]
    [StringLength(60)]
    public string FirstName { get; set; }

    [StringLength(60)]
    public string MiddleName { get; set; }

    [StringLength(60)]
    public string LastName { get; set; }

    [Required]
    public string Designation { get; set; }

    [Required]
    public double Salary { get; set; }
}
EF Core - Modified Database

Entity Framework Fluent API can also be used to configure DbContext classes to override conventions.

Summary

We covered how to implement Entity Framework Core in ASP.NET Web API with the code-first approach. I have even posted a video of the implementation.

One thing to keep in mind is that the entity framework doesn’t fit in all the scenarios so please do your research well before deciding to implement it in your project.

Referenceshttps://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro?view=aspnetcore-3.1

Source code download

  1. What is entity framework core?

    It is an open-source object-relational mapper (ORM) built over ADO.NET and an official data access platform from Microsoft.W

  2. Which databases are supported with Entity framework?

    Entity framework core can work with wide variety of databases. Microsoft provides rich providers for SQL Server & SQLite and there are many other third party providers some commercial and some from community.

  3. Is entity framework code asynchronous ?

    Asynchronous programming is the default mode for ASP.NET Core and Entity Framework Core.

Web API Versioning in ASP.NET Core 3.1

Web API versioning though being one of the important features mostly goes ignored when building a Web API application. Web API versioning in ASP.NET Core is simple and easy to implement. In this article, we will look at what is Web API versioning along with why it is needed and how to achieve Web API versioning in ASP.NET Core.

Web API Versioning – What & Why?

Web API Versioning in ASP.NET Core

When we start a new Web API project at that time it is all fine as we develop an initial version get it tested with all the clients and push it to production. Now after going to production there are some changes in existing Web API which is a mandatory change for one of the client but it will not be possible for other clients to go live with new changes at the same time. Also, it is not recommended to change existing working clients by implementing new Web API features which they don’t need. So now if you change the existing Web API on production with breaking changes and if any of the clients don’t implement new changes it will start failing. So now there is a need to support the old version as well as the newer version of Web API so that all existing clients can function without failing.

Web API versioning is a feature using which we can implement multiple versions of the same API so that different clients can work with the required version of API.

Implement Web API versioning in ASP.NET Core

There are many ways to implement versioning in Web API. Here I will be covering methods to version Web API using Nuget Package Microsoft.AspNetCore.Mvc.Versioning

We can achieve Web API versioning using following approaches

  1. Query String
  2. HTTP Header
  3. URL

Here for demonstration purposes, I have created an ASP.NET Core 3.1 Web API project. I will be using the default generated WeatherForecast Controller for configuring Web API Versioning. By default, there is no versioning support added as shown below.

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public string Get()
    {
        return "This is from WeatherForecast - Sunny Day";
    }
}

As shown below Install the Nuget package and enable the support for Web API versioning in ConfigureServices method in the startup.cs file.

Web API Versioning in ASP.NET Core 3
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddApiVersioning(apiVerConfig =>
        {
            apiVerConfig.AssumeDefaultVersionWhenUnspecified = true;
            apiVerConfig.DefaultApiVersion = new ApiVersion(new DateTime(2020, 6, 6));
        });

        services.AddControllers();
    }
    //Remaining code has been removed
}

Web API Versioning using Querystring Parameter

Here you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in querystring i.e. http://server:port/api/weatherforecast?api-version=1.0 or 2.0.

Below is the output screen from both version of API

Versioning Using Querystring

Web API Versioning using HTTP Header

For version to be part of HTTP header and enable controllers to read version number form header you need to first configure this in ConfigureServices method in startup.cs file as hows below

public void ConfigureServices(IServiceCollection services)
{
    services.AddApiVersioning(apiVerConfig =>
    {
        apiVerConfig.AssumeDefaultVersionWhenUnspecified = true;
        apiVerConfig.DefaultApiVersion = new ApiVersion(1,0);
        apiVerConfig.ReportApiVersions = true;
        apiVerConfig.ApiVersionReader = new HeaderApiVersionReader("api-version");
    });

    services.AddControllers();
}

Then you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in HTTP header as “api-version: 1.0” or “api-version: 2.0”

Below is the output screen from both version of API

Versioning Using HTTP Header

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Web API Versioning using URL

Here you need to define the same controller twice with different values for ApiVersion attribute in different namespaces as shown below. Also, you need to add parameter v{version:apiVersion} in route attribute like Route(“api/v{version:apiVersion}/[controller]”) so that API version becomes part of URL.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}

namespace WebAPIVersioning.Controllers.V2
{
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V2 - Rainy Day";
        }
    }
}

After defining multiple versions of the same controller as shown above you can invoke these using version number in HTTP URL i.e. http://server:port/api/v1/weatherforecast or http://server:port/api/v2/weatherforecast

Below is the output screen from both version of API

Versioning in ASP.NET Core using HTTP URL

Supported & Deprecated API Version

In startup.cs file while configuring API version since we have enabled parameter ReportApiVersions so any API call will return supported versions for that API in response header as shown below.

Supported API Versions

Now if you want to pass on the information that older version of API will be removed then you can add deprecate flag to the version of API which will be not supported in future.

namespace WebAPIVersioning.Controllers.V1
{
    [ApiVersion("1.0",Deprecated = true)]
    [Route("api/[controller]")]
    [ApiController]
    public class WeatherForecastController : ControllerBase
    {
        [HttpGet]
        public string Get()
        {
            return "This is from WeatherForecast V1 - Sunny Day";
        }
    }
}
deprecated API versions

Summary

API versioning is useful when you need to enhance an existing API that is being consumed by multiple clients. Versioning in ASP.NET Core Web API is easy and simple to implement. We can make use of the NuGet package Microsoft.AspNetCore.Mvc.Versioning to achieve the same.

Though there are a number of ways to achieve API versioning I personally prefer URL based versioning as that avoids extra api-version parameter in querystring and HTTP header both.

You can also check my other article on Top 12 ASP.NET Core libraries for developers: https://procodeguide.com/programming/top-12-essential-asp-net-core-libraries/

Dependency Injection in ASP.NET Core 3.1

ASP.NET Core is designed from the ground up to support the dependency injection design pattern. Dependency injection in ASP.NET Core provides a technique to create applications that are loosely coupled. ASP.NET Core allows us to injects dependent objects either through constructor or methods. Based on the environment in which application is running different objects can be injected.

What is Dependency Injection?

Dependency Injection in ASP.NET Core

Dependency injection is used to achieve loose coupling in application between client objects and their dependencies. Instead of directly instantiating objects of classes and using them rather Interfaces are used and objects are provided to class in some way either through constructor or through some method.

Class will depend on an interface rather than concrete implementation of dependencies. This makes our code loosely coupled as we can inject any concrete object as far as it implements the required interface.

ASP.NET Core comes with a built-in container (IServiceProvider) that supports constructor injection by default and the framework itself provides some services through dependency injection.

Here is a great article by Martin Fowler on Dependency Injection

Why Dependency Injection

  • Improves code readability by keeping it clean so easier to maintain it.
  • Application is loosely coupled which improves flexibility
  • Increases code testing ability with different mock implementations of services.
  • Supports extendable class structure

Implementation of Dependency Injection in ASP.NET Core

There are two types of services in ASP.NET Core

  1. Framework provided services – These services are part of ASP.NET Core framework
  2. Custom Services – These are add-on services created by developers

Registering framework provided services

Below code shows how to register a framework defined service (Memory Caching Service) in ConfigureServices() method of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();
    }
    //Remaining code was removed
}

Registering your own services

You can even register your own service and make it available for dependency injection into controllers, middlewares & views

First you need to define the service using an interface as shown below

public interface ILogger
{
    void Log(string message);
    void Log(string message, Exception ex);
}

public class LogToFile : ILogger
{
    public void Log(string message)
    {
        //Log Message to File
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to File
    }
}

public class LogToDB : ILogger
{
    public void Log(string message)
    {
        //Log Message to Database
    }

    public void Log(string message, Exception ex)
    {
        //Log Message & Exception to Database
    }
}

Service Lifetime

Before we talk about how to register your service in the startup.cs file we need to understand the significance of Service Lifetime while registering a service. Lifetime decides whether the instance of a service being injected is a new instance to that component or it same signal instance which is being used everywhere in the application. While registering service you need to choose the lifetime for each service.

Transient – Each time a new object is created for dependency injection i.e. each component will have its unique instance. This is the option used for lightweight & stateless services.

services.AddTransient<ILogger, LogToFile>();

Scoped – This means that it is created once for each request i.e. a new object will be created for each new request.

services.AddScoped<ILogger, LogToFile>();

Singleton – One single component will be created for the lifetime of the application. This single instance is shared across all components and all requests.

services.AddSingleton<ILogger, LogToFile>();

Registering Services in startup.cs

You need to register required services in ConfigureServices() methif of startup.cs

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddMemoryCache();

        services.AddTransient<ILogger, LogToDB>();
    }
     //Remaining code has been removed
}

Once service is registered it can be injected into other components

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

Injecting services (framework/custom) into Middleware

Below is the code sample for the injection of service into middleware. Either it can be injected through the constructor and or through the invoke method.

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public ExceptionMiddleware(RequestDelegate next, ILogger logger, IMemoryCache cache)
    {
        _next = next;
        _logger = logger;
        _cache = cache;
    }

    public Task Invoke(HttpContext httpContext, ILogger logger, IMemoryCache cache)
    {
        return _next(httpContext);
    }
}

Injecting services (framework/custom) into Controller

Below is the code sample for the injection of service into the controller. Either it can be injected through the constructor and or through the action method.

public class HomeController : Controller
{
    private readonly ILogger _logger;
    private readonly IMemoryCache _cache;

    public HomeController(ILogger logger, IMemoryCache cache)
    {
        _logger = logger;
        _cache = cache;
    }

    public IActionResult Index([FromServices] ILogger logger, [FromServices] IMemoryCache cache)
    {
        return View();
    }
}

Request Services Manually

Instead of using dependency injection to inject services, components can even manually request services. Below code shows how to manually request a service using HttpContext object.

var services = this.HttpContext.RequestServices;
var log = (ILogger)services.GetService(typeof(ILogger));

Summary

Dependency injection allows us to develop loosely coupled applications. Dependency injection in ASP.NET Core is supported and also it is simple and easy to implement. Framework provided services or your own custom services can be used for dependency injection.

You can inject components using dependency injection into middlewares, controllers, view, etc. Service lifetime can be used to control the scope of the service.

References: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1

You can also check my another Article on Microsoft Azure Storage – https://procodeguide.com/programming/azure-storage/

Top 12 ASP.NET Core libraries for developers

Top 12 ASP.NET Core Libraries

Many developers when they start working on ASP.NET Core they have the decision to make like which libraries are they going to use to get the code done. A list of Top 12 ASP.NET Core Libraries will allow developers to understand these so that they can pick appropriate libraries for their projects. Now really libraries to be selected will depend upon the requirements i.e. functionalities required in the application. But irrespective of the requirements there are some cross-cutting technologies that should be part of any application like security, logging, exception handling, etc.

For .NET many libraries are available from which developers can select. With the release of .NET Core mostly all of the library providers have started providing support for .NET Core. It is very important for the success of the project to pick rights libraries.

Here is my list of top 12 ASP.NET Core libraries which developers should know about so that they can decide which tools they are going to use for application development.

Here is the list of Top 12 ASP.NET Core Libraries

NUnit

Unit testing is an important part of software development & automated unit testing helps in getting the regression test done much quicker. NUnit is an open-source unit testing framework for .NET Core. It is one of the most popular frameworks available for unit testing your code.

NUnit does not create any test scripts by itself but it allows you to use its tools and classes to write test scripts for your code.

Learn more about NUnit in .NET Core

Auto Mapper

Let’s say you have two different classes EmployeViewModel & EmployeeDTO with the same data members and you need to copy data from one object to another. Now you cannot set an instance of one object to another object as though data member match but objects are of different classes. You will have to use the traditional approach i.e. copy data from one object to another field by field.

To simply this job of mapping data from one object to objects Auto Mapper should be used. Auto Mapper is an open-source library and It maps the properties of two different objects,

You can check my other article on How to implement Automapper in ASP.NET Core 3.1

More details on Auto Mapper

Newtonsoft Json.NET

It is an open-source high-performance JSON framework for .NET. This is very popular & easy to use as well. It helps you to serialize and deserialize any .NET object with JSON serializer i.e. it allows you to convert an object to & from JSON.

Click here for more details

Serilog

Serilog is a popular third party logging provider that is supported in ASP.NET Core Logging. It is very easy to set up and provides API for logging. Serilog provides a structured logging framework and supports a wide variety of sinks to log to console, files, azure, etc.

Serilog support structured logging that allows message string along with objects to be logged and these are logged in structured format which allows it easier to be queried by software program.

You can check my other article on ASP.NET Core Logging with Serilog

Entity Framework Core

Entity Framework Core is an object-relational mapper (O/RM) framework for .NET Core. It is a new version of entity framework after entity framework 6.x and is an open-source, lightweight & extensible. EF Core support many database engines.

ORM’s allows you to access and store data in the database using .NET objects and eliminated the need for traditional data access code which the developers used to write.

Here there is need to mention one more popular ORM library Dapper

You can check my other article on Entity Framework Core in ASP.NET Core 3.1

Health Checks

This library is available in ASP.NET core as Middleware for monitoring the health of the application. Health checks are exposed as HTTP endpoints by an application.

Health can be used by load-balancer to check application status and it can also be used to check parameters like memory, disk, connectivity with database or external services, etc.

More details on Health Checks

if you want to know more about Middleware then you can check my other article on ASP.NET Core Middleware

Newsletter Subscription

Stay updated! Instantly get notified about my new articles in your mailbox by subscribing via email

SignalR

If you want to push any data directly from the server to the client then this is the library for you. SignalR can be used for real time functionality applications like chat, games, stock exchange tickers, etc.

More details on SignalR

MiniProfiler

MiniProfiler is an open-source & lightweight library. It is used to monitor the performance of an .NET application. It helps in identifying application performance issues.

More details on MiniProfiler

Hangfire

It can be quite a challenge to perform a background processing or run the task at the scheduled time in the ASP.NET Core web application. But open-source library Hangfire allows you to easily perform these tasks. Hangfire provides an easy way to perform this scheduled background processing in ASP.NET Core.

Here there is need to mention one more similar library Quartz.

More details on Hangfire

RabbitMQ

RabbitMQ is a popular open-source message broker. RabbitMQ allows integration of distributed applications using Messages & Queues. It allows us to implement asynchronous messaging between applications.

More details on RabbitMQ

Polly

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Source: https://github.com/App-vNext/Polly

Polly is a open-source library to implement algorithms like retry logic, timeouts, circuit breakers, etc.

Swashbuckle

Swashbuckle is an open source library to generate swagger documentation for ASP.NET Core Web API. This documentation can be used to explore and test API.

More details on Swashbuckle

Conclusion

Here I have introduced to my list of Top 12 ASP.NET Core libraries for developers. I hope this will help you in selecting the right set of libraries for your project.