Redis Caching in ASP.NET Core- Depth in Distributed Caching

Redis Caching in ASP.NET Core- Depth in Distributed Caching

In this article, we will learn how to use Redis caching in an ASP.Net Core Web API. Before starting this article please must visit our previous article In-Memory Caching in ASP.NET Core where we discussed about Caching , In-Memory Caching in ASP.NET Core, and other concepts related to caching. In this article, we will talk about Distributed Caching, Redis, Setting up Redis Caching in ASP.NET Core.

Find SourceCode

Prerequisites

– Basic knowledge of Caching & In-memory of Caching
– Visual Studio 2019
– .NET Core SDK 5.0 ( You can use SDK 3.1 )
Redis Caching in ASP.NET Core- Depth in Distributed Caching
Tweet

What is Distributed Caching ?

A distributed cache is a cache shared by multiple app servers, typically maintained as an external service to the app servers that access it. A distributed cache can improve the performance and scalability of an ASP.NET Core app, especially when the app is hosted by a cloud service or a server farm.

Like an in-memory cache, it can improve the ASP.Net Core application response time quite drastically. However, the implementation of the Distributed Cache is application-specific. This means that there are multiple cache providers that support distributed caches. A few of the popular ones are Redis and NCache.

Pros of Distributed Caching

A distributed cache has several advantages over other caching scenarios where cached data is stored on individual app servers. When cached data is distributed, the data:

  • Is coherent (consistent) across requests to multiple servers.
  • Multiple Applications / Servers can use one instance of Redis Server to cache data. This reduces the cost of maintenance in the longer run.
  • The cache would not be lost on server restart and application deployment as the cache lives external to the application.
  • It does not use the local server’s resources.

Cons of Distributed Caching

There are two main disadvantages of the distributed caching:

  • The cache is slower to access because it is no longer held locally to each application instance.
  • The requirement to implement a separate cache service might add complexity to the solution

What is Redis Caching ?

Redis(Remote DIctionary Server) is an open source, in-memory data structure store, used as a database, cache and message broker. It supports data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes with radius queries and streams.

It’s a NoSQL Database as well and is being used at tech giants like Stackoverflow, Flickr, Github, and so on. Redis is a great option for implementing a highly available cache to reduce the data access latency and improve the application response time. As a result, we can reduce the load off our database to a very good extent.

Let’s understand in a better way of Redis cache,

  • User requests a user object.
  • App server checks if we already have a user in the cache and return the object if present.
  • App server makes a HTTP call to retrieve the list of users.
  • Users service returns the users list to the app server.
  • App server sends the users list to the distributed (Redis) cache.
  • App server gets the cached version until it expires (TTL).
  • User gets the cached user object.

IDistributedCache Interface

ASP.NET Core provides IDistributedCache interface to interact with the distributed caches including Redis.. IDistributedCache Interface provides you with the following methods to perform actions on the actual cache.

  • GetAsync – Gets the Value from the Cache Server based on the passed key.
  • SetAsync – Accepts a key and Value and sets it to the Cache server
  • RefreshAsync – Resets the Sliding Expiration Timer (more about this later in the article) if any.
  • RemoveAsync – Deletes the cache data based on the key.

How to setup Redis in a Windows Machine

To setup Redis in a windows machine, There is Redis open source Github Repo, You could also use the MSI Executable file.

Extract the highlighted zip folder and open redis-server.exe file.

Redis cache command

Now, the Redis server is up and running. We can test it using the redis-cli command.

Open a new command prompt and run redis-cli on it and try the following commands:

Now that we installed the Redis-server and saw that it is working properly, we can modify the API to use Redis-based distributed caching.

Redis CLI Commands

Setting a Cache Entry
-> Set name "corespider"
OK
Getting the cache entry
-> Get name
"corespider"
Deleting a Cache Entry
-> Get name
"corespider"
-> Del name
(integer)1

Integrating Redis Cache In ASP.NET Core

In the previous example of In-Memory Caching we created the ASP.NET Core Web API project. This API is connected to DB via Entity Framework Core and return the customer list.

MAKE Sure that Redis Server is up and running in the background.

  • Install the below package using Nuget Package manager console OR you can install package using Nuget search.
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
  • To support the Redis cache let’s configure the cache in startup.cs/ConfigureServices method.

By default, Redis runs on the local 6379 port. To change this, open up Powershell and run the following command.

./redis-server --port {your_port}

services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:4450";
});

Implement Redis Cache memory in Controller

        private readonly ApplicationDbContext _context;
        private readonly IMemoryCache memoryCache;
        private readonly IDistributedCache distributedCache;
        public CustomersController(ApplicationDbContext context, IMemoryCache memoryCache, IDistributedCache distributedCache)
        {
            _context = context;
            this.memoryCache = memoryCache;
            this.distributedCache = distributedCache;
        }
        // GET: api/Customers
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
        {
            var cacheKey = "Allcustomer";
            string serializedCustomerList;
            var customerList = new List<Customer>();
            var redisCustomerList = await distributedCache.GetAsync(cacheKey);
            if (redisCustomerList != null)
            {
                serializedCustomerList = Encoding.UTF8.GetString(redisCustomerList);
                customerList = JsonConvert.DeserializeObject<List<Customer>>(serializedCustomerList);
            }
            else
            {
                customerList = await _context.Customers.ToListAsync();
                serializedCustomerList = JsonConvert.SerializeObject(customerList);
                redisCustomerList = Encoding.UTF8.GetBytes(serializedCustomerList);
                var options = new DistributedCacheEntryOptions()
                    .SetAbsoluteExpiration(DateTime.Now.AddMinutes(10))
                    .SetSlidingExpiration(TimeSpan.FromMinutes(2));
                await distributedCache.SetAsync(cacheKey, redisCustomerList, options);
            }
            return customerList;
        }
  • Line #4-8: We use the required constructor in the controllers.
  • Line #14: We set the key internally in the code.
  • Line #17: Access the distributed cache object to get data from Redis using the key “AllCustomer”
  • Line #18 -23: If the key has a value in Redis, then convert it to a list of Customers and send back the data. If the value does not exist in Redis, then access the database via efcore, get the data and set it to redis.
  • Line #25-31: The data will be stored in Redis as a byte array. We will be converting this array of a string. Converts the string to an object of type List.
  • SlidingExpiration: Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration (if set).
  • AbsoluteExpiration: Gets or sets an absolute expiration date for the cache entry.

Test the Redis Cache in Postman

Let’s open the postman tool and test the cache performance.

In the first API call took about 6850 ms to return a response. During the first call the cache for customerList also would be set by our API onto the Redis server. Thus, the second call would be direct to the Redis cache, which should reduce the response time significantly. Let’s see that only takes 33 ms.

How to conditionally inject an IDistributedCache implementation of MemoryDistributedCache during local development?

You are using the key-value caching functionality of Redis. You are coding an Azure function (could be an ASP.NET Core Web app) and you want to develop and debug the code. Do you need to install Redis locally? Not neccessary. Through a couple of lines of clever DI, you can “fool” your classes to use the MemoryDistributedCache implementation of the interface IDistributedCache.

In this snippet we are checking for the the value of an environment variable and conditionally injecting the desired implementation of IDistributedCache

[TestMethod]
        public void Condition_Injection_Of_IDistributedCache()
        {
            var builder = new ConfigurationBuilder();
            builder.AddJsonFile("settings.json", optional: false);
            Config = builder.Build();
            ServiceCollection coll = new ServiceCollection();
            if (System.Environment.GetEnvironmentVariable("localdebug") == "1")
            {
                coll.AddDistributedMemoryCache();
            }
            else
            {
                coll.AddStackExchangeRedisCache(options =>
                {
                    string server = Config["redis-server"];
                    string port = Config["redis-port"];
                    string cnstring = $"{server}:{port}";
                    options.Configuration = cnstring;
                });
            }
            var provider = coll.BuildServiceProvider();
            var cache = provider.GetService<IDistributedCache>();
        }

How do we benchmark the performance of Redis cache?

The primary motivation for using a distributed cache is to make the application perform better. In most scenarios the central data storage becomes the bottleneck. Benchmarking the distributed cache gives us an idea of how much latency and throughput to expect from the cache for various document sizes.

Find Source Code

Summary

In this detailed article, we have learnt about Redis Caching in ASP.Net Core. We discussed Caching, the Basics, In-Memory Caching, Implementing In-Memory Caching in ASP.NET Core etc.

That’s a wrap for this article. I hope that it cleared about In-Memory Caching in ASP.NET Core-Basic guide of Caching. Please write to us if have any.

Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee

References

Latest Post

In-Memory Caching in ASP.NET Core-Basic guide of Caching
in memory caching

This article explain about In-Memory Caching in ASP.NET Core, We discussed here Caching, the Basics, In-Memory Caching, Implementing In-Memory Caching Read more

CQRS pattern with MediatR in ASP.NET Core 5.0
CQRS pattern with MediatR in ASP.NET Core 5.0

This article explain CQRS design pattern with MediatR in ASP.Net Core 5.0. CQRS is a design pattern that separated the Read more

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide
ASP.NET CORE WEB API 5.0 MONGODB CRUD

This article explain CRUD operation using MongoDB in ASP.NET Core Web API. We discuss here basics of MongoDB and how Read more

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0
Build CRUD REST APIs with ASP.NET Core 5.0

This article explains how to build CRUD REST APIs with ASP.NET Core 5.0 with EF core 5.0 and API secured Read more

In-Memory Caching in ASP.NET Core-Basic guide of Caching

In-Memory Caching in ASP.NET Core-Basic guide of Caching

In this article we will discuss about in depth about In-Memory Caching in ASP.Net Core. Here we discuss about all the possibilities of caching and will build a simple endpoint that can help demonstrate of cache entries from the in-memory Cache, and finally we will check after enabling the caching the application performance.

Find Source Code

Prerequisites

– Basic knowledge of Caching
– Visual Studio 2019
– .NET Core SDK 5.0 ( You can use SDK 3.1 )
In-Memory Caching in ASP.NET Core-Basic guide of Caching
Tweet

What is Caching ?

Caching is the process of storing copies of files in a cache, or temporary storage location, so that they can be accessed more quickly.

A cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. In simply we can say the most frequently used data are stored in a temporary storage so that it can be accessed much faster for the future calls from the client. This technique will boost the performance of the application drastically by removing unnecessary and frequent requests to the data source.

Caching can significantly improve the performance and scalability of an app by reducing the work required to generate content. Caching works best with data that changes infrequently and is expensive to generate. Caching makes a copy of data that can be returned much faster than from the source. Apps should be written and tested to never depend on cached data.

  • On left hand side, you can see every time the resources are called to the datasource that may burden to the server and counter performance issue.
  • Whereas on right side part, you can see when a user is call to a datasource then it cached and it provide the result set from cached datasource only not the main datasource, so from performance point of view it should be much faster.

Caching In ASP.NET Core

ASP.NET Core support for various types of caching as follows.

  1. In-Memory Caching – Where the data is cached within the server’s memory.
  2. Distributed caching – The data is stored external to the application in sources like Redis cache etc., we will discuss it later.

In-Memory Caching in ASP.NET Core ?

In ASP.NET Core, it is now possible to cache the data within the application, this is known as In-Memory Caching in ASP.NET Core. The simplest cache is based on the IMemoryCache. IMemoryCache represents a cache stored in the memory of the web server. Apps running on a server farm (multiple servers) should ensure sessions are sticky when using the in-memory cache. Sticky sessions ensure that subsequent requests from a client all go to the same server.

In-memory caching is a service that’s referenced from an app using Dependency Injection. So, we first need to register this service to the built-in IoC container of ASP.NET Core by modifying ConfigureServices method of Startup.cs.

Cache Implementation in ASP.NET Core application

We have created the ASP.NET Core web API application that have set up an API and configured Entity Framework Core. This API will return a list of all customers in the database. If you need to learn about setting up Entity Framework Core in ASP.NET Core, we recommend you to go through this article – CRUD Operation in ASP.NET Web API with Entity Framework.

In this example we created a ASP .NET Core web API 5.0 project and connect with EF along with customer database.

In-Memory Caching in ASP.NET Core is a Service that should be registered in the service container of the application.

services.AddMemoryCache();

The above line of code should be add on configure services. This adds a non-distributed, in-memory implementation to the IServiceCollection. Now our Application eligible to provide in-memory caching.

Implement Cache in Controller Endpoint

[Route("api/[controller]")]
    [ApiController]
    public class CustomersController : ControllerBase
    {
        private readonly ApplicationDbContext _context;
        private readonly IMemoryCache memoryCache;
        public CustomersController(ApplicationDbContext context, IMemoryCache memoryCache)
        {
            _context = context;
            this.memoryCache = memoryCache;
        }
        // GET: api/Customers
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Customer>>> GetCustomers()
        {
            var cacheKey = "Allcustomer";
            if (!memoryCache.TryGetValue(cacheKey, out List<Customer> customerList))
            {
                customerList = await _context.Customers.ToListAsync();
                var cacheExpiryOptions = new MemoryCacheEntryOptions
                {
                    AbsoluteExpiration = DateTime.Now.AddMinutes(5),
                    Priority = CacheItemPriority.High,
                    SlidingExpiration = TimeSpan.FromMinutes(2)
                };
                memoryCache.Set(cacheKey, customerList, cacheExpiryOptions);
            }
            return customerList;
        }
      }
  }

Code Explanation

  • Line #6 – IMemoryCache definition
  • Line #16 – Here we are setting the cache key internally in our code.
  • Line #17 – If there is a cache entry with the key as “Allcustomer”, then push this data to a List object. Else, if there is no such cache entry, call the database via the context object and set this value as a new cache entry along with the expiration parameters. The expiration date is according to you, you can according to your requirements.
  • SlidingExpiration: Gets or sets how long a cache entry can be inactive (e.g. not accessed) before it will be removed. This will not extend the entry lifetime beyond the absolute expiration (if set).
  • AbsoluteExpiration: Gets or sets an absolute expiration date for the cache entry.

A cached item set with a sliding expiration only is at risk of becoming stale. If it’s accessed more frequently than the sliding expiration interval, the item will never expire. Combine a sliding expiration with an absolute expiration to guarantee that the item expires once its absolute expiration time passes

Test the In-Memory Cache in Postman

Let’s open the postman tool and test the cache performance.

You can see when I call the customer API, it fetch around 50-60 records to take time 9391 ms.

In this call, we are directly calling the database, which may be slow depending on the traffic, connection status, size of the response, and so on. We have configured our API to parallelly store these records to the cache as well.

So in the second call, the response is expected at much more better time.

As you can see, on second API call it takes 39 ms. You can see the drastic improvement in the performance, this is how awesome Caching is.

Pros of In-Memory Cache

  • Much Quicker than other forms of distributed caching as it avoids communicating over a network.
  • Highly Reliable and best suited for Small to Mid Scale Applications.

Cons of In-Memory Cache

  • If configured incorrectly, it can consume your server’s resources.
  • With the scaling of application and longer caching periods, it can prove to be costly to maintain the server.
  • If deployed in the cloud, maintaining consistent caches can be difficult.

Points To Remember about Caching

  • Application should never depend on the Cached data as it is highly probable to be unavailable at any given time. Traditionally it should depend on your actual data source. Caching is just an enhancement that is to be used only if it is available/valid.
  • Try to restrict the growth of the cache in memory. This is crucial as Caching may take up your server resources if not configured properly. You can make use of the Size property to limit the cache used for entries.
  • Use Absolute Expiration / Sliding Expiration to make your application much faster and smarter. It also helps restricts cache memory usage.
  • Try to avoid external inputs as cache keys. Always set your keys in code.

How to improve Cache Entry

To do improvements on caching, we can use Background Jobs to update cache at a regular interval. If the Absolute Cache Expiration is set to 10 minutes, then we can can run a recurring job every 11 minutes of interval to update the cache entry. You can use Hangfire to achieve the same in ASP.NET Core Applications.

Find source code

Summary

In this detailed article, we have learnt about In-Memory Caching in ASP.Net Core. We discussed Caching, the Basics, In-Memory Caching, Implementing In-Memory Caching in ASP.NET Core etc.

That’s a wrap for this article. I hope that it cleared about In-Memory Caching in ASP.NET Core-Basic guide of Caching. Please write to us if have any.

Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee

Redis Caching in ASP.NET Core- Depth in Distributed Caching
Redis Caching in ASP.Net Core

This article explain about Redis Caching in ASP.NET Core, We discuss about Distributed Caching, Redis, Setting up Redis Caching in Read more

CQRS pattern with MediatR in ASP.NET Core 5.0
CQRS pattern with MediatR in ASP.NET Core 5.0

This article explain CQRS design pattern with MediatR in ASP.Net Core 5.0. CQRS is a design pattern that separated the Read more

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide
ASP.NET CORE WEB API 5.0 MONGODB CRUD

This article explain CRUD operation using MongoDB in ASP.NET Core Web API. We discuss here basics of MongoDB and how Read more

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0
Build CRUD REST APIs with ASP.NET Core 5.0

This article explains how to build CRUD REST APIs with ASP.NET Core 5.0 with EF core 5.0 and API secured Read more

CQRS pattern with MediatR in ASP.NET Core 5.0

CQRS pattern with MediatR in ASP.NET Core 5.0

In this article we discuss about CQRS pattern with MediatR in ASP.Net Core. We discuss about more details on CQRS design pattern, and how to implement this pattern in a real world example with MediatR at .NET Core. CQRS is one of the most commonly used patterns that helps architect the Solution to accommodate the Onion Architecture in .NET Core. Please follow our previous article on ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide.

Find Source Code

What are the issue in Traditional Architecture?

  • In traditional architectures, the same data model is used to query and update a database. That’s simple and works well for basic CRUD operations.
  • In complex applications this approach can become unwieldy. For example, on the read side, the application may perform many different queries, returning data transfer objects (DTOs) with different shapes. Object mapping can become complicated. On the write side, the model may implement complex validation and business logic. As a result, you can end up with an overly complex model that does too much.
  • Read and write workloads are often asymmetrical, with very different performance and scale requirements.
  • The traditional approach can have a negative effect on performance due to the load on the data store and data access layer, and the complexity of queries required to retrieve information.
  • Managing security and permissions can become complex because each entity is subject to both read and write operations, which might expose data in the wrong context.

What is CQRS Pattern ?

CQRS(Command Query Responsibility Segregation) is a design pattern that separated the read and write operations of a data source, hence decoupling the solution to a great extend. Command refers to a Database Command, which can be either an Insert / Update or Delete Operation, whereas Query stands for Querying data from a source.

CQRS allows an application to work with different models, One model that has data needed to update a record, another model to insert a record, yet another to query a record. This gives us flexibility with varying and complex scenarios. You don’t have to rely on just one DTO for the entire CRUD Operations by implementing CQRS.

CQRS_Architecture

Pros of CQRS

  • Optimized Data Transfer Objects: One model per data operation that gives us all the flexibility to get rid of the complex model class.
  • Highly Scalable: As full control over the model classes in CQRS the data operations makes application highly scalable in the long run.
  • Improved Performance: In all enterprises application there are always Read operations is more compared to write operation. using this pattern we should speed up the performance on the read operations by introducing a cache or NOSQLDB.
  • Secure Parallel Operations: Since we have dedicated models per operation, there is no possibility of data loss while doing parallel operations.

Cons of CQRS

  • Code complexation: As we are introducing a new design pattern so we may write more code lines to integrate CQRS.

What is MediatR Pattern ?

In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program’s running behavior. In object-oriented programming, programs often consist of many classes.

In simply we can see Mediator pattern is another design pattern that dramatically reduces the coupling between various components of an application by making them communicate indirectly, usually via a special mediator object. We will dive deep into Mediator in another post. Essentially, the Mediator pattern is well suited for CQRS implementation.

Creating ASP.NET Core project and Implement CQRS Pattern

Let’ s build an ASP.NET Core WebAPI 5.0 project to implementation on CQRS Pattern. We build an ASP.NET Core Web API endpoint that does CRUD operations for a Transaction Entity, i.e Create/ Update/ Delete product record from the Database.

In this example we consider a Payment account Transaction CRUD operation and see how to segregate the CRUD using CQRS in MediatR.

  • Open Visual studio 2019 and create ASP.NET Core Web API project.
  • The target framework is .NET 5.0 and tick on Enable OpenAPI support to enable swagger.
CQRS_Architecture Web API

Install Packages to implement CQRS

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Relational
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package MediatR
Install-Package MediatR.Extensions.Microsoft.DependencyInjection
Install-Package Microsoft.EntityFrameworkCore.Tools

Adding the Transaction Model

Let’s add the transaction model inside Entity folder.

 public class Transaction
    {
        public int TransactionId { get; set; }
        public string AccountNumber { get; set; }
        public string BeneficiaryName { get; set; }
        public string BankName { get; set; }
        public string SWIFTCode { get; set; }
        public int Amount { get; set; }
        public DateTime Date { get; set; }
    }

Adding the Context Class and Interface

  • Create a new Folder called Context and add a class named as TransactionDBContext. This class enables to access the data using EF core ORM. Also create the interface ITransactionDbContext.
public interface ITransactionDbContext
    {
        DbSet<Transaction> Transactions { get; set; }
        Task<int> SaveChanges();
    }
 public class TransactionDbContext : DbContext, ITransactionDbContext
    {
        public TransactionDbContext(DbContextOptions<TransactionDbContext> options) : base(options)
        { }

        public DbSet<Transaction> Transactions { get; set; }
        public async Task<int> SaveChanges()
        {
            return await base.SaveChangesAsync();
        }
    }

Define the Connection String in appsettings.json

We define a connection string in the appsettings.json found within the API Project.

"ConnectionStrings": {
    "connectionstring": "Server=SEREVERNAME;Database=paymentDB;Trusted_Connection=True;MultipleActiveResultSets=true"
  }

Configuring the API Services in Startup

Navigate to your API Project’s Startup class and add these lines to your Startup Class ConfigureServices method. This will register the EF Core with the application.

services.AddDbContext<TransactionDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("connectionstring"),
                    b => b.MigrationsAssembly(typeof(TransactionDbContext).Assembly.FullName)));

Generating the Database using EF Core

To generate the database use the Package Manager Console of Visual Studio. You can open this by going to Tools -> Nuget Package Manager -> Package Manager Console.

add-migration "initial"

After adding the above command you can find the new folder with Migrations is created on below.

CQRS Architecture

Then update database using below command.

update-database

After executing the command you can see now it create database with migrations.

CQRS_Architecture Entity framework transaction

Configuring MediatR in ASP.NET Core Web API Project

Mediator pattern is another design pattern that dramatically reduces the coupling between various components of an application by making them communicate indirectly. MediatR is a library that helps implements Mediator Pattern in .NET.

To register the library, we need to add at the end of our API startup class in the ConfigureService Method.

services.AddMediatR(Assembly.GetExecutingAssembly());

Implement the CRUD Operations

Let’s implement CRUD(Create, Read, Update, and Delete) operations using MediatR pattern.

CQRS_Architecture Command and Queries

Let’s Create a Folder name as Features and under this folder can create another folder name as TranscationFeatures and then create subfolders named as the Queries and Command.

You can create Other Features according to your requirements.

Commands

The commands folder only consist of methods those are use on transaction related to database. Let’s add the classes to TranscationFeatures Commands folder.


1.CreateTransactionCommand
2.UpdateTransactionCommand
3.DeleteTranscationByIdCommand

Create Transaction Command
 public class CreateTransactionCommand : IRequest<int>
    {
        public int TransactionId { get; set; }
        public string AccountNumber { get; set; }
        public string BeneficiaryName { get; set; }
        public string BankName { get; set; }
        public string SWIFTCode { get; set; }
        public int Amount { get; set; }
        public DateTime Date { get; set; }
        public class CreateProductCommandHandler : IRequestHandler<CreateTransactionCommand, int>
        {
            private readonly ITransactionDbContext _context;
            public CreateProductCommandHandler(ITransactionDbContext context)
            {
                _context = context;
            }
            public async Task<int> Handle(CreateTransactionCommand command, CancellationToken cancellationToken)
            {
                var tran = new Transaction();
                tran.AccountNumber = command.AccountNumber;
                tran.BeneficiaryName = command.BeneficiaryName;
                tran.BankName = command.BankName;
                tran.SWIFTCode = command.SWIFTCode;
                tran.Amount = command.Amount;
                tran.Date = command.Date;
                _context.Transactions.Add(tran);
                await _context.SaveChanges();
                return tran.TransactionId;
            }
        }
    }
  • #3-9 – We create model property to create transaction command.
  • #12-16 – Constructor Inject the database context.
  • #17-29 – Assign the model that push into the transactions table.

Update Transaction Command
public class UpdateTransactionCommand : IRequest<int>
    {
        public int Id { get; set; }
        public string AccountNumber { get; set; }
        public string BeneficiaryName { get; set; }
        public string BankName { get; set; }
        public string SWIFTCode { get; set; }
        public int Amount { get; set; }
        public DateTime Date { get; set; }
        public class UpdateTransactionCommandHandler : IRequestHandler<UpdateTransactionCommand, int>
        {
            private readonly ITransactionDbContext _context;
            public UpdateTransactionCommandHandler(ITransactionDbContext context)
            {
                _context = context;
            }
            public async Task<int> Handle(UpdateTransactionCommand command, CancellationToken cancellationToken)
            {
                var tran = _context.Transactions.Where(a => a.TransactionId == command.Id).FirstOrDefault();
                if (tran == null)
                {
                    return default;
                }
                else
                {
                    tran.AccountNumber = command.AccountNumber;
                    tran.BeneficiaryName = command.BeneficiaryName;
                    tran.BankName = command.BankName;
                    tran.SWIFTCode = command.SWIFTCode;
                    tran.Amount = command.Amount;
                    tran.Date = command.Date;
                    await _context.SaveChanges();
                    return tran.TransactionId;
                }
            }
        }
    }
Delete Transaction Command
public class DeleteTranscationByIdCommand : IRequest<int>
    {
        public int Id { get; set; }
        public class DeleteProductByIdCommandHandler : IRequestHandler<DeleteTranscationByIdCommand, int>
        {
            private readonly ITransactionDbContext _context;
            public DeleteProductByIdCommandHandler(ITransactionDbContext context)
            {
                _context = context;
            }
            public async Task<int> Handle(DeleteTranscationByIdCommand command, CancellationToken cancellationToken)
            {
                var transcation = _context.Transactions.Where(a => a.TransactionId == command.Id).FirstOrDefault();
                if (transcation == null) return default;
                _context.Transactions.Remove(transcation);
                await _context.SaveChanges();
                return transcation.TransactionId;
            }
        }
    }
  • Delete transaction takes place considering the transaction by ID.

Queries

The Queries folder only consist of methods that are fetched from database only means not any READ/WRITE operation to database.

Get All Transaction Query
public class GetAllTranscationsQuery : IRequest<IEnumerable<Transaction>>
    {
        public class GetAllTranscationsQueryHandler : IRequestHandler<GetAllTranscationsQuery, IEnumerable<Transaction>>
        {
            private readonly ITransactionDbContext _context;
            public GetAllTranscationsQueryHandler(ITransactionDbContext context)
            {
                _context = context;
            }
            public async Task<IEnumerable<Transaction>> Handle(GetAllTranscationsQuery query, CancellationToken cancellationToken)
            {
                var transcationList = await _context.Transactions.ToListAsync();
                if (transcationList == null)
                {
                    return null;
                }
                return transcationList.AsReadOnly();
            }
        }
    }
  • Line #5-9: Constructor Inject the DB Context interface.
  • Line #10-18 : Here we make all the transaction details that are available in DB.

Get Transaction By Id
    public class GetTranscationByIdQuery : IRequest<Transaction>
    {
        public int Id { get; set; }
        public class GetTranscationByIdQueryHandler : IRequestHandler<GetTranscationByIdQuery, Transaction>
        {
            private readonly ITransactionDbContext _context;
            public GetTranscationByIdQueryHandler(ITransactionDbContext context)
            {
                _context = context;
            }
            public async Task<Transaction> Handle(GetTranscationByIdQuery query, CancellationToken cancellationToken)
            {
                var transcation = _context.Transactions.Where(a => a.TransactionId == query.Id).FirstOrDefault();
                if (transcation == null) return null;
                return transcation;
            }
        }
    }
  • This return values by transaction Id.

Create the Transaction API Controller

Let’s create a transaction API controller that make the CRUD operation using the Feature service of Commands and Queries.

 [Route("api/[controller]")]
    [ApiController]
    public class TranscationController : ControllerBase
    {
        private IMediator _mediator;
        protected IMediator Mediator => _mediator ??= HttpContext.RequestServices.GetService<IMediator>();
        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            return Ok(await Mediator.Send(new GetAllTranscationsQuery()));
        }
        [HttpGet("{id}")]
        public async Task<IActionResult> GetById(int id)
        {
            return Ok(await Mediator.Send(new GetTranscationByIdQuery { Id = id }));
        }
        [HttpPost]
        public async Task<IActionResult> Create(CreateTransactionCommand command)
        {
            return Ok(await Mediator.Send(command));
        }
        [HttpPut("{id}")]
        public async Task<IActionResult> Update(int id, UpdateTransactionCommand command)
        {
            if (id != command.Id)
            {
                return BadRequest();
            }
            return Ok(await Mediator.Send(command));
        }
        [HttpDelete("{id}")]
        public async Task<IActionResult> Delete(int id)
        {
            return Ok(await Mediator.Send(new DeleteTranscationByIdCommand { Id = id }));
        }
    }
  • Line #5-6: MediatR interface is injected.
  • Line #7-35 : We create the CRUD Operation call. Here Create, Update, Delete is call to commands and for Read the transaction call from Queries.

Configure Service method in Startup

   public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TransactionDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("connectionstring"),
                    b => b.MigrationsAssembly(typeof(TransactionDbContext).Assembly.FullName)));
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "CQRS_MediatR", Version = "v1" });
            });
            services.AddControllers(); 
            services.AddScoped<ITransactionDbContext>(provider => provider.GetService<TransactionDbContext>());
            services.AddMediatR(Assembly.GetExecutingAssembly());
        }

Test the CQRS with MediatR using Swagger

Let’s test our CRUD application using Swagger. Build & Run the application

CQRS_Architecture Swagger

POST method test using swagger

Let’s test the POST method using swagger with required inputs like below and then hit execute button.

CQRS_Architecture Swagger Post

Now when you navigate to database and we can see the transaction is added in the table.

GET method test using swagger

Try to execute the GET method we can see the result.

You can test the CRUD using Swagger using this way.

Source Code

Please find source code that are available in GitHub.

Source Code

Summary

In this article, we discussed CQRS pattern with MediatR in ASP.Net Core 5.0, also discussed what is CQRS Pattern and MediatR and how to implement it in ASP.NET Core CRUD.

Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee

CQRS pattern with MediatR in ASP.Net Core

In-Memory Caching in ASP.NET Core-Basic guide of Caching
in memory caching

This article explain about In-Memory Caching in ASP.NET Core, We discussed here Caching, the Basics, In-Memory Caching, Implementing In-Memory Caching Read more

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide
ASP.NET CORE WEB API 5.0 MONGODB CRUD

This article explain CRUD operation using MongoDB in ASP.NET Core Web API. We discuss here basics of MongoDB and how Read more

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0
Build CRUD REST APIs with ASP.NET Core 5.0

This article explains how to build CRUD REST APIs with ASP.NET Core 5.0 with EF core 5.0 and API secured Read more

How to send Email in ASP.NET Core-Beginner’s Guide
How to send Email in ASP.NET Core

This article explain how to send Email in ASP.NET Core using Mailkit. It is open-source cross-platform library that can be Read more

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide

In this article we discuss about how to use MongoDB in ASP.NET Core Web API. It’s complete beginners tutorial to learn how we can integrate mongoDB in ASP.NET Core web API project. Now-a-days we hear a common word about NoSQL databases. These are being very strongly promoted by all vendors. One of the most common NoSQL databases these days is MongoDB. In this article we will discuss what a NoSQL database is, when it must be used and then we will look at an example to install, create and populate this database. Finally, we will look at how to access data from this database using a ASP.NET Core Web API. Also please read our previous article How to send Email in ASP.NET Core-Beginner’s Guide.

Navigate to source code

What is a NoSQL database?

NoSQL database is a collection of documents with no real defined structure as in an RDBMS or SQL database. That means that we do not have any pre-defined columns as we do have in a SQL table. We simply store each row with its own number of columns or data fields as we may call them.

Advantages of NoSQL database?

  • The common advantages of NoSQL database is the data for each document is in one place and faster to read.
  • As there is no pre-defined structure we can store documents with different fields in one place and do not have to worry about adding new columns each time where most rows could contain NULLS as in a SQL database. Also, it saves us from creating new tables with relationships.

What is MongoDB?

MongoDB is one of the most common NoSQL databases. MongoDB can be downloaded and run from your desktop computer. It runs as a service. It also comes with a client called “MongoDB Compass Community”. This is a nice compact client which allows you to connect to the instance running, view the databases, collections, and documents inside it. It also allows you to create new databases, new collections inside the database, and new documents inside the collection using the JSON format. Being a Microsoft developer, another well known NoSQL database is Cosmos DB in addition to the tables storage mechanism in Microsoft Azure.

Installing MongoDB Server

MongoDB allows to host our own instance locally in our infrastructure. They offer Enterprise as well as community editions of their installable server. You will have to install both the server and a GUI Application (MongoDB Compass) to manage the MongoDB, very similar to SQL Management Studio.

Download MongoDB server.
MongoDb downlead

Once you have downloaded the MSI package, you can install it as shown below:

mongoDB msi file

After installation, Open up MongoDB Compass. With Compass, you could specify the connection string to any authorized server and start browsing the database quite easily, But since we have no connection string with us, we can connect to the local MongoDB Server. For this you don’t have to specify the connection string, just press CONNECT.

This gets you connected to the localhost:27017 port where your default mongodb service is running.

mongoDb compass

After click on connect you can navigate to this below screen.

Create Database

Input database name as per your choice and set the collection name.

Let’s try to add a new Document to the Books Collection. Select the Books Collection and Add Data -> Insert Document.

{
  "name": "Clean Architecture in .NET Core",
  "price" : 1000,
  "category" : "Computers",
  "author" : "JT"
}

After click on insert button the JSON collection data is inserted in Books collection. You can note that the ID gets generated automatically with a random GUID.

MongoDB In ASP.NET Core Web API – Getting Started

MongoDB is highly cross platform and can be seamlessly integrated with ASP.NET Core to take the maximum advantage of this awesome document based NOSQL Database. Let’s walkthrough step by step on working with MongoDB in ASP.NET Core. We will be setting up MongoDB Server and ultimately building a ASP.NET Core 5.0 WebAPI that does CRUD operations on a MongoDB Instances.

Create ASP.NET Core Web API project

Open Visual stuidio and create a new ASP.NET Core Web API and select target framework as 5.0 and also tick Open API support to enable swagger on our project. Using swagger we can test the API.

Add MongoDB Driver package in the project

Let’s add MongoDB package using package manager console.

Install-Package MongoDB.Driver

Create the Model class to manage CRUD

Let’s create the model class inside the Entity folder named as Books.

public class Books
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId)]
        public string id { get; set; }
        public string name { get; set; }
        public int price { get; set; }
        public string category { get; set; }
        public string author { get; set; }
    }
  • Line no. 3&4 – For Id property, we set the primary key of MongoDB document, this is represented as [BsonId]. MongoDB recognizes object Id and not string/int as the primary key. Thus we have to manually set the property so that MongoDB converts the string to object Id.
  • Line no. 5-9 – The properties of Books collection that we are added.

Add Connection string in Appsettings.json file

"BooksConnectionSettings": {
    "BooksCollectionName": "Books",
    "ConnectionString": "mongodb://localhost:27017",
    "DatabaseName": "BooksDB"
  }
  • Line #1 : Connection setting name declare.
  • Line #2-4 : We declared connection collection name, connection string and Database name.

Add a configuration class

Let’s add a configuration class inside configuration folder.

public class BooksConfiguration
    {
        public string BooksCollectionName { get; set; }
        public string ConnectionString { get; set; }
        public string DatabaseName { get; set; }
    }

Add the Service class to complete CRUD

Let’s create a Interface to hold the CRUD operation method.

public interface IBookService
    {
        Task<List<Books>> GetAllAsync();
        Task<Books> GetByIdAsync(string id);
        Task<Books> CreateAsync(Books book);
        Task UpdateAsync(string id, Books book);
        Task DeleteAsync(string id);
    }

Create the Book service class and implement the IBookService inside it.

using CoreAPI.Mongo.Configuration;
using CoreAPI.Mongo.Entity;
using MongoDB.Driver;
public class BookService: IBookService
    {
        private readonly IMongoCollection<Books> _book;
        private readonly BooksConfiguration _settings;
        public BookService(IOptions<BooksConfiguration> settings)
        {
            _settings = settings.Value;
            var client = new MongoClient(_settings.ConnectionString);
            var database = client.GetDatabase(_settings.DatabaseName);
            _book = database.GetCollection<Books>(_settings.BooksCollectionName);
        }
        public async Task<List<Books>> GetAllAsync()
        {
            return await _book.Find(c => true).ToListAsync();
        }
        public async Task<Books> GetByIdAsync(string id)
        {
            return await _book.Find<Books>(c => c.id == id).FirstOrDefaultAsync();
        }
        public async Task<Books> CreateAsync(Books book)
        {
            await _book.InsertOneAsync(book);
            return book;
        }
        public async Task UpdateAsync(string id, Books book)
        {
            await _book.ReplaceOneAsync(c => c.id == id, book);
        }
        public async Task DeleteAsync(string id)
        {
            await _book.DeleteOneAsync(c => c.id == id);
        }
    }
  • Line #3 – Defining the MongoCollection of Books.
  • Line #4 – Since we are using the IOptions Pattern to read our MongoDB Connection Configuration from the appsetting.json, we will have to inject the configuration class to the constructor.
  • Line #8 – Injecting the IOptions of the Configuration to the constructor.
  • Line #12-13 – Getting the Configuration Information and initializing the Books collection.
  • Line #15-35 – Here we added the CRUD methods.

Add the Dependency Injection inside startup

Add the Dependency Injection inside ConfigureServices class on startup class.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "CoreAPI.Mongo", Version = "v1" });
            });
            services.Configure<BooksConfiguration>(Configuration.GetSection("BooksConnectionSettings"));
            services.AddScoped<IBookService, BookService>();
        }
  • Line #4-7 – Swagger configuration.
  • Line #8 – Configuration to read the connections.
  • Line #9 – We inject the service class.

Create Books Controller to implement CRUD

public class BookController : ControllerBase
    {
        private readonly IBookService _bookService;
        public BookController(IBookService bookService)
        {
            _bookService = bookService;
        }
        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            return Ok(await _bookService.GetAllAsync());
        }
        [HttpGet]
        public async Task<IActionResult> Get(string id)
        {
            var book = await _bookService.GetByIdAsync(id);
            if (book == null)
            {
                return NotFound();
            }
            return Ok(book);
        }
        [HttpPost]
        public async Task<IActionResult> Create(Books book)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest();
            }
            await _bookService.CreateAsync(book);
            return Ok(book.id);
        }
        [HttpPut]
        public async Task<IActionResult> Update(string id, Books booksData)
        {
            var book = await _bookService.GetByIdAsync(id);
            if (book == null)
            {
                return NotFound();
            }
            await _bookService.UpdateAsync(id, booksData);
            return NoContent();
        }
        [HttpDelete]
        public async Task<IActionResult> Delete(string id)
        {
            var book = await _bookService.GetByIdAsync(id);
            if (book == null)
            {
                return NotFound();
            }
            await _bookService.DeleteAsync(book.id);
            return NoContent();
        }
    }
  • Line #3-6 – Inject the constructor dependency.

Testing the CRUD using Swagger

Let’s run the application and we should test the application using swagger.

Creating a New Book collection

Let’s create a POST request JSON like below and hit execute button.

And you can see now the new book item has been added into our collection in MongDB database.

Read all book Collection

Get Books by Id

To get a Book by Id, send a GET Request to the same endpoint, but with an additional parameter, the Book id.

Update Books Data

Send the Book id as the parameter of the endpoint and also the Book data to be updated.

And you can see the update data is there.

Delete Book Data

To delete a Books data, simply send in the id as the parameter with DELETE Request like below.

Now, let’s refresh the MongoDB compass and we can see that the book data is removed from the collection.

Find Source Code

Please find the source code in GitHub.

Navigate source code

Summary

In this article, we covered the basic concepts of NOSQL Databases with mongoDB in ASP.NET Core Web API, we discuss here Introduction to MongoDB, Installing MongoDB. We built an ASP.NET Core 5.0 WebAPI that demonstrates the integration of MongoDB in ASP.NET Core seamlessly.

Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee

How to send Email in ASP.NET Core-Beginner’s Guide
How to send Email in ASP.NET Core

This article explain how to send Email in ASP.NET Core using Mailkit. It is open-source cross-platform library that can be Read more

CQRS pattern with MediatR in ASP.NET Core 5.0
CQRS pattern with MediatR in ASP.NET Core 5.0

This article explain CQRS design pattern with MediatR in ASP.Net Core 5.0. CQRS is a design pattern that separated the Read more

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0
Build CRUD REST APIs with ASP.NET Core 5.0

This article explains how to build CRUD REST APIs with ASP.NET Core 5.0 with EF core 5.0 and API secured Read more

Toast Notifications in ASP.NET Core Razor Pages
Toast Notifications in ASP.NET Core Razor Pages

This article we will learn how simply we can use Toast Notifications in ASP.NET Core Razor Pages 5.0. Mainly on Read more

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0

Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0

This article explains how to build CRUD REST APIs with ASP.NET Core 5.0 with Entity Framework 5.0 code first approach. Also we learn how to secure APIs using JWT authentication token and test the CRUD APIs using POSTMAN. Please have a look our previous article Migrate from ASP.NET Core 3.1 to 5.0- Beginners guide.

Find the source code

Prerequisites

–  Visual Studio 2019 16.8 or later
.NET 5.0 SDK
– SQL Server 2014 (You can use higher version of SQL)
– Postman to test API
Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0
Tweet

What is REST API ?

REST stands for representational state transfer. REST APIs are the HTTP-based protocol that provides to communicate with other application using lightweight JSON format. As you know in current era there is no. of clients (mobile apps, browser-based SPAs, desktop apps, IOT apps, etc.), we need better ways for transferring data from servers to clients, independent of technology and server stacks. In this case REST API is the good benchmark among the application.

What is JWT Token ?

JWT stands for JSON Web Token, It is open standard and transfer data securely between two entities (client and server). A JWT is digitally signed using a secret key by a token provider or authentication server. A JWT helps the resource server verify the token data using the same secret key, so that you can trust the data.

JWT consists of the following three parts:

Header: encoded data of token type and the algorithm used to sign the data.

Payload: encoded data of claims intended to share.

Signature: created by signing (encoded header + encoded payload) using a secret key.

The final JWT token will be like this: Header.Payload.Signature. In below we described how JWT works.

Step 1: Client requesting token

The client sends a request to the authentication server with the necessary information like username and password to prove its identity. If the credential is valid then the server create a token with the necessary claims, and a JWT token will be sent back to the client.

Step 2: Client sends token to resource server

Client Stores the token temporarily and for each request to Resource or the API server, the client needs to include a token in the header and request the resource using its URI. Then the Server access the token decode and validate it.

Step 3: Resource server verifies the token

To verify the token below are the few steps.

  • Read the token from authentication header then Split the header, payload and signature from token.
  • Create signature of received header and payload using the same secret key used when creating the token.
  • Check whether both newly created signature and signature received from token are valid.
  • If the signatures are the same, the tokens are valid (not altered in the middle) and they provide access to the requested resource.
  • If the signatures are different, an unauthorized response will be sent back to the client. (In the middle, if claims are alerted, they will generate a different signature, hence resource access will be restricted.)

Create an ASP.NET Core 5.0 REST API application

To create an ASP.NET Core 5.0 Rest API application follow below steps.

  • Open Visual Studio 2019, Create New Project
  • Choose ASP.NET Core Web Application and then set Project location and project path.
  • Select ASP.NET Core SDK 5.0 and then select ASP.NET Core Web API project.

Install required Nuget Packages

Install below required packages using package manager console or you can install using Manage Nuget packages.

Install-Package Microsoft.EntityFrameworkCore-Version 5.0.0
Install-Package Microsoft.EntityFrameworkCore.Sqlite-Version 5.0.0
Install-Package Microsoft.EntityFrameworkCore.SqlServer-Version 5.0.0
Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design-Version 1.1.6
Install-Package Microsoft.EntityFrameworkCore.Tools-Version 5.0.0
Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design-Version 5.0.0

Creating the Model Class

To do EF core 5.0 we do create the model class that treated as entity of product object and user object that should be done database operations. Right click on the Models folder and add the class name with following properties.

Product.cs

[Table("Products")]
    public class Product
    {
        [Key]
        public int ProductId { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        [Column(TypeName = "decimal(18,2)")]
        public decimal Price { get; set; }
        public int Quantity { get; set; }
        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public DateTime CreateDate { get; set; }
    }
  • The product model consist the object of product information.
  • [Table(“Products”)] is belong to System.ComponentModel.DataAnnotations.Schema namespace. Here Products is the table name that we want to create dynamically using EF code first.
  • ProductId is the primary key of the table object.
  • Price column is set as default value.
  • CreateDate column is the computed value means it set as default date for this field.

User.cs

[Table("Users")]
    public class User
    {
        public int UserId { get; set; }
        public string FullName { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
        [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
        public DateTime CreateDate { get; set; }
    }
  • The user model consist the object of user information.
  • [Table(“Users”)] is belong to System.ComponentModel.DataAnnotations.Schema namespace. Here Users is the table name that we want to create dynamically using EF code first.
  • UserId is the primary key of the table object.
  • CreateDate column is the computed value means it set as default date for this field.
  • User table is use as JWT claim and role based.

After creating the Models class must rebuild the applications. To rebuild the project right click on Project and then click Rebuild.

The user database script look like below.

SET IDENTITY_INSERT [dbo].[Users] ON 

INSERT [dbo].[Users] ([UserId], [FullName], [UserName], [Password], [Email], [CreateDate], [UserRole]) VALUES (1, N'John Doe', N'john', N'john$321', N'john.doe@gmail.com', CAST(N'2020-11-26T00:00:00.0000000' AS DateTime2), N'Admin')
SET IDENTITY_INSERT [dbo].[Users] OFF

Adding the Product API Controller for CRUD REST API

To do database operations to create, update, read and delete over model class we need to create the controller that should do the CRUD operations for us. To create the Controller right click on the Controllers folder and add a new Controller and in the prompt choose as API controller with actions, using Entity framework like below. Here we can use in-built entity framework scaffold technique to create CRUD query for us.

After select the API controller with actions, using Entity framework click the OK button then a new prompt comes again.

  • We select model class is product as we create the model class Product CRUD.
  • DataContext we select as ProductContext, you may change it according to your choice.
  • Then our controller name is ProductsController.

After create the scaffold controller automatically it create the CRUD ( Create, Read, Update, Delete ) operations using Customer Model class and ProductContext class like below.

ProductContext.cs

public class ProductContext : DbContext
    {
        public ProductContext (DbContextOptions<ProductContext> options)
            : base(options)
        {
        }
        public DbSet<ProductCoreService_DOTNET5.Models.Product> Product { get; set; }
        public DbSet<ProductCoreService_DOTNET5.Models.User> User { get; set; }
    }

And it automatically add the DBContext inside the service class of startup.cs like below.

Also the connection string automatically add in appsettings.json file like below. ProductContext is the connection string and value you can modify according to your DB connection.

"ConnectionStrings": {
    "ProductContext": "Server=(localdb)\\mssqllocaldb;Database=ProductContext-2e02db2f-41e9-4ba7-9b78-d0733c05e2b5;Trusted_Connection=True;MultipleActiveResultSets=true"
  }

ProductController.cs

The following REST APIs are created:

  • To list all products: HTTP Get method
  • Get product detail: HTTP Get method
  • Update product detail: HTTP Put method
  • Create product: HTTP Post method
  • Delete product: HTTP Delete method
 [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ProductContext _context;

        public ProductsController(ProductContext context)
        {
            _context = context;
        }

        // GET: api/Products
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
        {
            return await _context.Product.ToListAsync();
        }

        // GET: api/Products/5
        [HttpGet("{id}")]
        public async Task<ActionResult<Product>> GetProduct(int id)
        {
            var product = await _context.Product.FindAsync(id);

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

            return product;
        }

        // PUT: api/Products/5
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPut("{id}")]
        public async Task<IActionResult> PutProduct(int id, Product product)
        {
            if (id != product.ProductId)
            {
                return BadRequest();
            }

            _context.Entry(product).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!ProductExists(id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }

            return NoContent();
        }

        // POST: api/Products
        // To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
        [HttpPost]
        public async Task<ActionResult<Product>> PostProduct(Product product)
        {
            _context.Product.Add(product);
            await _context.SaveChangesAsync();

            return CreatedAtAction("GetProduct", new { id = product.ProductId }, product);
        }

        // DELETE: api/Products/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteProduct(int id)
        {
            var product = await _context.Product.FindAsync(id);
            if (product == null)
            {
                return NotFound();
            }

            _context.Product.Remove(product);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        private bool ProductExists(int id)
        {
            return _context.Product.Any(e => e.ProductId == id);
        }
    }

Entity Framework Code First Migrations

Code first migrations here help you to create the database for us with following tables Products and Users.

  • Open Package manager console of the project and type the command Add-Migration and hit enter.
  • After executing the command it adds a new file with the name “Initial” prefixed with the date time stamp. It prefixes the date time stamp so that it could track the various migrations added during development and segregate between those. Open the file and we see the class named “Initial” deriving from DbMigration class. This class contains two methods that are overridden from DbMigration class i.e. the base class. The method names are Up() and Down().
  • The final command that creates the database and respective tables out of our context and model. It executes the Initial migration that we added and then runs the seed method from the configuration class. This command is smart enough to detect which migrations to run. For example it will not run previously executed migrations and all the newly added migrations each time will be taken in to account to be executed to update the database. It maintains this track as the database firstly created contains an additional table named __MigrationHistory that keeps track of all the migrations done.
  • The command is Update-Database

After executing the update-database command the database is created like below and now we can test REST CRUD API using Postman tool.

Test REST API CRUD operation using Postman

Before starting to test the API we need to set the route page like below in launchSettings.json

Create product: HTTP Post method

Open the Postman tool and run the application and here we want to create the customer data that means we push the customer information so like below we can push the data.

  • Here http://localhost:49243/api/Customers is the URL to create CRUD operation.
  • Here we want push the information to database so need to choose POST method in Postman.
  • In the Body section append the inputs in json format as formatted above.
  • Click the send button and then the data is pushed to database using post method like below;
  • After click the send button the product data is entered into database using POST method like below.
[HttpPost]
   public async Task<ActionResult<Product>> PostProduct(Product product)
    {
      _context.Product.Add(product);
      await _context.SaveChangesAsync();
      return CreatedAtAction("GetProduct", 
              new { id = product.ProductId }, product);
   }
  • Enter this endpoint into Postman: https://localhost:44305/api/products.
  • Choose POST method and set as header: ‘Content-Type’: ‘application/json’.
  • Under Body Raw, choose type JSON (application/javascript) and paste the product details.

To Get list of all products: HTTP Get method

To fetch all the products that we need to change only method as GET instead of POST.

// GET: api/Products
        [HttpGet]
        public async Task<ActionResult<IEnumerable<Product>>> GetProduct()
        {
            return await _context.Product.ToListAsync();
        }

You can see that doing change is the GET method you can fetch all the product list that inserted in our database.

HERE THE API IS NOT SECURE. That means anybody can access our API without any authentication.

We need to secure the WEB API in different type of authentication methodology. Here we discuss how to secure ASP.NET Core Web API 5.0 using JWT authentication.

Secure ASP.NET Core Web API 5.0 using JWT authentication

Following below steps we can add JWT authentication in ASP .NET Core Web API.

Step-1: Add the below JWT configuration in appsetting.json file

"JwtConfig": {
    "Key": "wRJSMeKKF2QT4fwpMeJf36PO1NiIsInR5cCI6IkNTY3ODkwIG9lIiwiaWF0IjoxN",
    "Issuer": "ProductAuthServer",
    "Audience": "ProductAuthClient",
    "Subject": "ProductAuthAccessToken"
  }
Step-2: Create a controller and check users credentials

Create a controller named as TokenController to perform following operations.

  • Accept username and password as input.
  • Check users credentials with database to ensure users’ identity.
    • If it is valid then access token will be returned.
    • If it is not valid then bad request will be return.
 [Route("api/[controller]")]
    [ApiController]
    public class TokenController : ControllerBase
    {
        public IConfiguration _configuration;
        private readonly ProductContext _context;
        public TokenController(IConfiguration config, ProductContext context)
        {
            _configuration = config;
            _context = context;
        }
        [HttpPost]
        public async Task<IActionResult> Post(User _user)
        {

            if (_user != null && _user.UserName != null && _user.Password != null)
            {
                var user = await GetUser(_user.UserName, _user.Password);

                if (user != null)
                {
                    //create claims details based on the user information
                    var claims = new[] {
                    new Claim(JwtRegisteredClaimNames.Sub, _configuration["JwtConfig:Subject"]),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                    new Claim(JwtRegisteredClaimNames.Iat, DateTime.UtcNow.ToString()),
                    new Claim("Id", user.UserId.ToString()),
                    new Claim("FullName", user.FullName),
                    new Claim("UserName", user.UserName),
                    new Claim("Email", user.Email),
                    new Claim("UserRole", user.UserRole)
                   };

                    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtConfig:Key"]));

                    var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

                    var token = new JwtSecurityToken(_configuration["JwtConfig:Issuer"], _configuration["JwtConfig:Audience"], claims, expires: DateTime.UtcNow.AddDays(1), signingCredentials: signIn);

                    return Ok(new JwtSecurityTokenHandler().WriteToken(token));
                }
                else
                {
                    return BadRequest("Invalid credentials");
                }
            }
            else
            {
                return BadRequest();
            }
        }

        private async Task<User> GetUser(string username, string password)
        {
            return  _context.User.FirstOrDefault(u => u.UserName == username && u.Password == password);
        }
    }

  • Using JWT claim it creates the token and the below line of code are responsible to create the JWT token.
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtConfig:Key"]));
var signIn = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_configuration["JwtConfig:Issuer"], _configuration["JwtConfig:Audience"], claims, expires: DateTime.UtcNow.AddDays(1), signingCredentials: signIn);

Check the JWT token endpoint using Postman

To check the JWT token endpoint in Postman we need to follow the below step.

  • The endpoint is https://localhost:57431/api/token.
  • Choose the POST method and set the header to ‘Content-Type’: ‘application/json’.
  • Choose type JSON in the body section and pass the user information.
  • User credentials is check, and a token will be generated and returned like below.
JWT-token-create

Secure the API endpoint of Product controller

As we have already create the JWT token, then we have secure the endpoint using below steps.

Add the below namespace in startup.cs file.

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
Configure authorization middleware in the startup configureService method.

 services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidAudience = Configuration["JwtConfig:Audience"],
                    ValidIssuer = Configuration["JwtConfig:Issuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtConfig:Key"]))
                };
            });
  • Here we passed the security key used when creating the token, and we have also enabled validation of Issuer and Audience.
  • Also, we have set SaveToken to true, which stores the bearer token in HTTP Context. So we can access the token in the controller when needed.
Inject the authorization middleware into the Request pipeline in Configure method like below..

JWT authorize
Add the authorization attribute in Product controller, after adding the [Authorize] attribute it secured with the token.

Authorize controller

Test secured REST API using Postman

To fetch the list of product details let’s run the API using postman.

  • Run the API URL http://localhost:49243/api/Products in the postman and see that all list of product are fetch or not.
  • Now you can see that the response status code is 401 Unauthorized. The anonymous access has been blocked and the APIs have been secured. Let’s understand how to access this secure API controller.

How to access secured API passing JWT token

We have already create the JWT token, so we need to pass the authentication token inside in authorization header like below,

Run the Token controller to generate the JWT token. (http://localhost:57431/api/token)

Prepend the text Bearer with this token, the token is as follows.

Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJQcm9kdWN0QXV0aEFjY2Vzc1Rva2VuIiwianRpIjoiNTQ4ODlkMzgtNzM4MS00YjE1LTkyM2UtZjY3ZTI1YWQwOTg0IiwiaWF0IjoiMTIvOS8yMDIwIDU6NDk6MDUgUE0iLCJJZCI6IjEiLCJGdWxsTmFtZSI6IkpvaG4gRG9lIiwiVXNlck5hbWUiOiJqb2huIiwiRW1haWwiOiJqb2huLmRvZUBnbWFpbC5jb20iLCJVc2VyUm9sZSI6IkFkbWluIiwiZXhwIjoxNjA3NjIyNTQ1LCJpc3MiOiJQcm9kdWN0QXV0aFNlcnZlciIsImF1ZCI6IlByb2R1Y3RBdXRoQ2xpZW50In0.-v90K5lXun9bmxkeitCd1syoz8Sv3Ebcr5fF2ZbYtp4

Under the Authorization header pass the token in the headers part, and then click Send.

  • When we pass the authorization header to the API, authentication middleware parses and validates the token. If it is found valid, it will set Identity.IsAuthenticated to true.
  • The Authorize attribute added in the controller will check whether the request is authenticated. If it is true, the API can be accessed.
  • If Identity.IsAuthenticated returns false, a 401 unauthorized error will be returned.

Boom ! Now you can see the JWT token is working perfectly. If the token is valid then it return appropriate result else return 401 unauthorized.

Find the Source Code on Github

Summary

This article explain how to create a REST API using ASP.NET Core 5.0 and Entity Framework Core to perform basic CRUD operations, create a JWT token, and secure the API.

That’s a wrap for this article. I hope that it cleared about Build CRUD REST APIs with ASP.NET Core 5.0 with EF Core 5.0. Please write to us if have any.

Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee


Related Post

In-Memory Caching in ASP.NET Core-Basic guide of Caching
in memory caching

This article explain about In-Memory Caching in ASP.NET Core, We discussed here Caching, the Basics, In-Memory Caching, Implementing In-Memory Caching Read more

CQRS pattern with MediatR in ASP.NET Core 5.0
CQRS pattern with MediatR in ASP.NET Core 5.0

This article explain CQRS design pattern with MediatR in ASP.Net Core 5.0. CQRS is a design pattern that separated the Read more

ASP.NET Core Web API with MongoDB CRUD- Beginner’s Guide
ASP.NET CORE WEB API 5.0 MONGODB CRUD

This article explain CRUD operation using MongoDB in ASP.NET Core Web API. We discuss here basics of MongoDB and how Read more

How to send Email in ASP.NET Core-Beginner’s Guide
How to send Email in ASP.NET Core

This article explain how to send Email in ASP.NET Core using Mailkit. It is open-source cross-platform library that can be Read more

Pin It on Pinterest