How to Implement NCache in ASP.NET Core Web API

How to Implement NCache in ASP.NET Core Web API

In this article, I will discuss How to Implement NCache in ASP.NET Core Web API Application with Examples. Please read our previous articles discussing How to Download and Install NCache in Windows OS.

NCache in ASP.NET Core Web API

NCache is a high-performance, distributed caching system specifically designed for .NET and .NET Core applications. It speeds up applications by reducing the time needed to access data from slower storage systems like databases or web services. In the context of ASP.NET Core, NCache effectively improves the scalability and performance of web applications by reducing the load on databases and providing faster access to frequently used data.

As an in-memory distributed cache, NCache stores frequently accessed data across multiple networked computers. This allows for fast retrieval of data, helping to speed up application performance and manage large volumes of data more efficiently.

NCache Features:
  • High Availability: It uses a cluster of cache servers to ensure that data is always available, even if one or more servers fail. If one server fails, the data is still accessible from other servers, minimizing the risk of data loss.
  • In-Memory Storage: NCache stores data directly in memory, which provides faster access compared to disk-based storage. This speeds up the retrieval of data and enhances the application’s performance.
  • ASP.NET Core Integration: NCache provides seamless integration with ASP.NET Core applications. It supports session state caching and application data caching, which helps maintain web applications’ performance and responsiveness under load-balancing environments.
  • Flexible Data Caching: It supports various caching strategies, such as absolute expiration, sliding expiration, and priority-based eviction policies, which helps manage the lifecycle of the cached data effectively.
  • SQL-like Queries: NCache allows querying of the cached objects using an SQL-like syntax, making it easier to retrieve complex data structures from the cache.
Where NCache Stores Data?

NCache primarily stores data in the memory of the cache servers. This approach ensures rapid access to cached data significantly faster than disk-based storage systems. NCache’s in-memory nature allows it to serve high-speed data access to applications.

  • Main Storage: NCache’s primary storage is the RAM of the servers where the cache is deployed, including all nodes in the cache cluster.
  • Overflow and Backing Storage: In configurations where persistence or overflow to disk is set up, NCache can also store data on disk. This is usually configured for scenarios where data durability is critical or when the amount of data exceeds the available RAM.
  • Client Cache: NCache also supports client caching, where frequently accessed data can be stored locally on the client machine. This client cache acts as a local in-memory cache reducing the number of trips made to the server for data retrieval, further enhancing performance.

Example to Understand NCache in ASP.NET Core Web API Project:

Let’s get started with implementing Redis Cache in the ASP.NET Core Web API Application. Let’s create a new ASP.NET Core Web API (using .NET 6) project to learn how to use NCache in ASP.NET Core. I will create a simple Web API that will use Entity Framework Core to query and return the Products from the SQL Server database.

Note: The latest version of NCache Packages is incompatible with .NET 8, so I am using .NET 6 to create the Web API Project.

Open Visual Studio and then choose “Create a new project“. Select “ASP.NET Core Web API” Project template. Configure your project settings (name such as NCacheDemo, location, etc.). Ensure “Configure for HTTPS” is checked. Click on the “Create” button which should create the Project with the following structure.

Add Necessary Packages:

Next, we need to add the following packages as we interact with the SQL Server database using Entity Framework Core. You can install these packages using the following command in the Package Manager Console. Please Install the Packages that are compatible with .NET 6. Don’t install the latest Package.

Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools

Define the Database Model and DbContext

In our example, we need to fetch the product master data. First, create a new folder named Models. Inside the Models folder, create a class file named Product.cs, and then copy and paste the following code.

namespace NCacheDemo.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string? Name { get; set; }
        public string? Category { get; set; }
        public int Price { get; set; }
        public int Quantity { get; set; }
    }
}
Create DbContext:

Next, we need to create the DbContent class. First, create a new folder named Data. Inside the Data folder, create a class ApplicationDbContext.cs and then copy and paste the following code.

using Microsoft.EntityFrameworkCore;
using NCacheDemo.Models;

namespace NCacheDemo.Data
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        public DbSet<Product> Products { get; set; }
    }
}
Configure Connection String:

Next, we need to add the connection string in the appsettings.json file. So, modify the appsettings.json file as follows. Here, the Entity Framework Core will create the database named ProductsDB if it has not already been created in the SQL server.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=ProductsDB;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
Configure the DbContext in the Program.cs:

Please add the following line to the Program.cs class file to configure DbContext.

// Configure DbContext with SQL Server
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

Note: Set the InvariantGlobalization value to False in the Project Properties file.

Create Database Migration:

Open the Package Manager Console and execute the Add-Migration command to create a new Migration file. Then, execute the Update-Database command to apply the migration to the database as follows.

How to Implement NCache in ASP.NET Core Web API Application with Examples

Once you execute the above command, it should create the ProductsDB database with the Products table, as shown in the below image:

How to Implement NCache in ASP.NET Core Web API Application with Examples

Insert Dummy Data:

You can insert dummy data directly into your database using SQL Server Management Studio or through seeding in EF Core. Please execute the following SQL Script in SSMS to insert some dummy data into the Products database table.

USE ProductsDb;
GO

DECLARE @i int = 1;
DECLARE @name varchar(50);
DECLARE @category varchar(50);
DECLARE @price int;
DECLARE @quantity int;

WHILE @i <= 1000
BEGIN
    -- Generate a random product name
    SET @name = CONCAT('Product ', @i);

    -- Randomly assign a category
    SET @category = CASE WHEN ABS(CHECKSUM(NEWID())) % 3 = 0 THEN 'Fruits'
                         WHEN ABS(CHECKSUM(NEWID())) % 3 = 1 THEN 'Vegetables'
                         ELSE 'Beverages' END;

    -- Generate a random price between 10 and 1000
    SET @price = ABS(CHECKSUM(NEWID())) % 991 + 10;  -- From 10 to 1000

    -- Generate a random quantity between 10 and 200
    SET @quantity = ABS(CHECKSUM(NEWID())) % 191 + 10;  -- From 10 to 200

    -- Insert the generated data into the Products table
    INSERT INTO Products (Name, Category, Price, Quantity)
    VALUES (@name, @category, @price, @quantity);

    SET @i = @i + 1;
END
Explanation:
  • USE ProductsDb; – Specifies the database to use.
  • GO – Batches the SQL commands for SQL Server.
  • DECLARE – Initializes the variables used in the script.
  • WHILE loop – Repeats the insertion process 1000 times.
  • NEWID() and CHECKSUM() – Functions used to generate random values.
  • INSERT INTO – Adds a row to the Products table with the specified values.
Using NCache in ASP.NET Core

Before connecting to our NCache server, we first need to install the NuGet packages. Let us install the Packages using the Package Manager Console. Please execute the following command to Install the Packages:

Alachisoft.NCache.SDK

Configuration of NCache in ASP.NET Core Web API

Configure the NCache client in your appsettings.json or through any other configuration method supported by ASP.NET Core. This typically involves specifying the cache servers, cache name, and other settings like security credentials. Please modify the appsettings.json file as follows:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=ProductsDB;Trusted_Connection=True;TrustServerCertificate=True;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "NCacheSettings": {
    "CacheName": "MyClusteredCache1",
    "Server": "192.168.29.83"
  },

  "AllowedHosts": "*"
}
Here,
  • CacheName: CacheName is the identifier for a particular cache in NCache that you want to connect to. This name is defined when you create a cache cluster in NCache. Each cache can have its own settings, stored items, and policies. So, the value of CacheName should be the name of the cache you have set up in your NCache cluster.
  • Server: Server refers to the network name or IP address of the server where the NCache service is running. This could be a single server or a load-balanced network address if you are connecting to a clustered cache. If you are using multiple servers in a clustered environment, you might specify just one of them or use a configuration that distributes the connections across them.

We got the Cache Name and Server from the NCache server. Please visit (http://localhost:8251/) URL and get the values as follows:

Configuration of NCache in ASP.NET Core Web API

Registering NCache Caching in Program Class

Set up dependency injection for NCache in the Program.cs class file. This allows you to use caching throughout your application easily. So, please add the following code to the Program.cs class file:

// Register NCache client
builder.Services.AddSingleton<ICache>(provider =>
{
    var cacheName = builder.Configuration.GetValue<string>("NCacheSettings:CacheName");
    ICache cache = CacheManager.GetCache(cacheName);
    return cache;
});
Using NCache in Controller and Services in ASP.NET Core

Create a new Web API controller named ProductsController inside the Controllers folder. Inject the ApplicationDbContext and ICache into the controller constructor and return all products from the GetAll method.

using Alachisoft.NCache.Client;
using Alachisoft.NCache.Runtime.Caching;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NCacheDemo.Data;
using NCacheDemo.Models;

namespace RedisCachingDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ApplicationDbContext _context;

        private readonly ICache _cache;
        public ProductsController(ApplicationDbContext context, ICache cache)
        {
            _context = context;
            _cache = cache;
        }

        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            var cacheKey = "GET_ALL_PRODUCTS";
            var products = _cache.Get<List<Product>>(cacheKey);

            if (products == null)
            {
                products = await _context.Products.ToListAsync();
                var cacheItem = new CacheItem(products)
                {
                    Expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(10))
                };

                // Add CacheItem to cache
                _cache.Insert(cacheKey, cacheItem);
            }

            return Ok(products);
        }
    }
}
How to Update and Delete NCache key in ASP.NET Core Web API

In an ASP.NET Core Web API, updating and deleting cache entries in NCache is straightforward. Please modify the Products Controller as follows, which shows updating and deleting cache keys when using NCache.

using Alachisoft.NCache.Client;
using Alachisoft.NCache.Runtime.Caching;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NCacheDemo.Data;
using NCacheDemo.Models;

namespace RedisCachingDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private readonly ApplicationDbContext _context;

        private readonly ICache _cache;
        public ProductsController(ApplicationDbContext context, ICache cache)
        {
            _context = context;
            _cache = cache;
        }

        [HttpGet]
        public async Task<IActionResult> GetAll()
        {
            var cacheKey = "GET_ALL_PRODUCTS";
            var products = _cache.Get<List<Product>>(cacheKey);

            if (products == null)
            {
                products = await _context.Products.ToListAsync();
                var cacheItem = new CacheItem(products)
                {
                    Expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(10))
                };

                // Add CacheItem to cache
                _cache.Insert(cacheKey, cacheItem);
            }

            return Ok(products);
        }

        // GET: api/Products/5
        [HttpGet("{id}")]
        public async Task<IActionResult> Get(int id)
        {
            var cacheKey = $"Product_{id}";
            
            // Get data from cache
            Product product = _cache.Get<Product>(cacheKey);

            if (product == null)
            {
                // If not found, then fetch data from database
                product = await _context.Products.FirstOrDefaultAsync(prd => prd.Id == id) ?? new Product();

                // set cache options 
                var cacheItem = new CacheItem(product)
                {
                    Expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(10))
                };

                // Add CacheItem to cache
                _cache.Insert(cacheKey, cacheItem);
            }

            return Ok(product);
        }

        // UPDATE: api/Products/5
        [HttpPut("{id}")]
        public async Task<IActionResult> UpdateProduct(int id, Product product)
        {
            if (id != product.Id)
            {
                return BadRequest();
            }

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

            try
            {
                await _context.SaveChangesAsync();

                var cacheKey = $"Product_{id}";

                // set cache options 
                var cacheItem = new CacheItem(product)
                {
                    Expiration = new Expiration(ExpirationType.Sliding, TimeSpan.FromMinutes(10))
                };

                // Add CacheItem to cache
                _cache.Insert(cacheKey, cacheItem);
            }
            catch (DbUpdateConcurrencyException ex)
            {
                if (!ProductExists(id))
                {
                    return NotFound();
                }
                else
                {
                    var customResponse = new
                    {
                        Code = 500,
                        Message = "Internal Server Error",
                        // Do not expose the actual error to the client
                        ErrorMessage = ex.Message
                    };
                    return StatusCode(StatusCodes.Status500InternalServerError, customResponse);
                }
            }

            return NoContent();
        }

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

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

            var cacheKey = $"Product_{id}";
            
            // Remove cache
             _cache.Remove(cacheKey);

            return NoContent();
        }

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

Now, run the application and test the API Endpoints, and it should work as expected.

What are the Differences Between Local Cache and Distributed Cache in NCache?

A Local Cache resides on the same machine as your application and is ideal for single-node applications. It offers fast access but limited scalability and high availability. On the other hand, a Distributed Cache like NCache stores data across multiple nodes, which provides high availability, fault tolerance, and scalability but might introduce more complexity in data synchronization and network overhead.

How Do We Secure Cache Data in NCache in ASP.NET Core?

Security is crucial when handling sensitive data in a cache to prevent unauthorized access and data leaks.

  • Encryption: Encryption protects sensitive data stored in the cache. NCache supports both encryption of the data at rest and secure transmission using SSL/TLS.
  • Access Control: Implement access controls to restrict who can access or modify the cache data.
  • Secure Configuration: Avoid storing sensitive information in configuration files, or ensure they are securely managed and encrypted if necessary.
Why We Need to Use NCache in ASP.NET Core Web API?

Integrating NCache into an ASP.NET Core Web API can significantly enhance the performance and scalability of your applications. Here’s why using NCache is particularly beneficial:

  • Improved Performance: NCache stores data in RAM, making data access times much faster than retrieving data from a disk-based database. This is crucial for high-performance applications that need to serve data rapidly and handle large volumes of requests.
  • Reduced Database Load: By caching data that is frequently accessed and doesn’t change often (like user sessions, application configurations, and static content), NCache reduces the number of calls made to the database. This not only speeds up data retrieval times but also reduces the load on your database, which can be a significant cost and resource benefit.
  • High Availability: NCache provides a distributed cache system, which means that the data is replicated across multiple servers. This replication ensures that if one server goes down, the cache is still available from other servers, thus enhancing your application’s fault tolerance.
  • Scalability: As your user base grows, NCache can scale out by adding more servers to the cache cluster. This scalability feature allows your application to handle more requests and manage more data without degrading performance.
  • Real-time Data Access: NCache supports real-time data updates and synchronization across multiple clients and servers. This is essential for applications where data consistency and real-time access are critical, such as financial applications or real-time monitoring systems.
  • Customizable Caching Strategies: NCache allows you to implement various caching strategies, such as absolute expiration, sliding expiration, and priority eviction. This flexibility helps optimize cache usage according to the specific needs and behaviors of your application.
  • Ease of Integration and Use: NCache provides support and easy integration with ASP.NET Core, simplifying its use in Web API projects. It can be seamlessly integrated into existing or new applications with minimal changes to the codebase.
  • Session State Management: In web applications, managing session state across multiple servers can be challenging. NCache can store session data in its distributed cache, maintaining session continuity and consistency across multiple web servers.

In the next article, I will discuss Response Caching in ASP.NET Core Web API Applications with Examples. In this article, I explain How to Implement NCache in an ASP.NET Core Web API Application with Examples. I hope you enjoy this article, How to Implement NCache in ASP.NET Core Web API Application.

Leave a Reply

Your email address will not be published. Required fields are marked *