Back to: ASP.NET Core Web API Tutorials
How to Implement In-Memory Caching in ASP.NET Core Web API
In this article, I will discuss how to Implement In-Memory Caching in ASP.NET Core Web API applications with examples. Please read our previous articles discussing Caching in ASP.NET Core Web API with Examples.
What is In-Memory Caching in ASP.NET Core Web API?
In-Memory Caching in ASP.NET Core is a mechanism for storing frequently accessed data in the server’s memory (RAM) to improve application performance and reduce database load. When a client makes a request that involves data retrieval, especially data that does not change often (e.g., master data like countries, states, cities), caching that data in memory helps the application to quickly serve subsequent requests without repeatedly querying the database.
Key Points About In-Memory Caching:
The following are the Key Points that you need to remember when working with In-Memory Caching in ASP.NET Core Web API Application:
- Server-Side Storage: Data is stored in the web server’s memory where the application is running.
- Faster Lookups: Serving data from memory (RAM) is typically faster than making network or database round-trips.
- Limited By Server Memory: Data is lost if the server restarts or the application pool recycles. Also, large caches can consume significant memory of the web server.
- Best For Small or Moderate Data Sets: In-memory caching is ideal when the volume of cached data is not excessively large and does not require sharing across multiple servers.
- When to Use: In scenarios where we have a single server hosting the API, the data access is read-heavy, and the data changes infrequently.
- When Not To Use: If you have a load-balanced or distributed environment requiring shared cache across many servers, consider a distributed cache solution like Redis or SQL Server distributed caching.
How Does In-Memory Caching Work in ASP.NET Core Web API?
For a better understanding of How In-Memory Caching Works in ASP.NET Core Web API Applications, please have a look at the following diagram:
Work Flow of In-Memory Caching in Web API:
- Client Request: The process starts when a client sends a request to the API.
- API Endpoint: The request is received by the API endpoint, which then processes the incoming request.
- Cache Check (“Cache Exists?”): The API checks whether the requested data is available in the in-memory cache.
-
- If the data exists, the API retrieves it from the cache.
- If the data is not found in the cache, the API queries the data source. After fetching, it stores the result in the cache.
-
- Return Data: Finally, the data is returned to the client.
Example to Understand In-Memory Caching in ASP.NET Core Web API
Imagine a scenario where your application needs to fetch master data, such as a list of countries, states, or cities. This data changes infrequently, so repeatedly querying the database for it would waste resources and increase response times. In-memory caching stores this master data in memory, allowing your application to quickly retrieve it without querying the database every time. As a result, it significantly reduces database round-trips, improving application performance and reducing database load. Let us proceed and see how to implement In-Memory Caching in ASP.NET Core Web API Application.
Create ASP.NET Core Web API Project and Add Necessary Packages
Open Visual Studio and create a new ASP.NET Core Web API project, giving the project a Name InMemoryCachingDemo. This project will serve as the base for implementing in-memory caching. As we will use Entity Framework Core with an SQL Server database, please install the following NuGet packages by executing the following commands in the Visual Studio Package Manager Console. These packages provide the necessary tools to work with EF Core and SQL Server.
- Install-Package Microsoft.EntityFrameworkCore.SqlServer
- Install-Package Microsoft.EntityFrameworkCore.Tools
Define the Models
In our example, we need to fetch the Country, State, and City master data, so we need three models. First, create a new folder named Models in the project root directory. Inside the Models folder, create three classes: Country, State, and City. Then, define the relationships using EF Core navigation properties.
Country.cs
Create a class file named Country.cs within the Models folder and then copy and paste the following code.
namespace InMemoryCachingDemo.Models { public class Country { public int CountryId { get; set; } public string Name { get; set; } // Navigation property to states belonging to this country public List<State> States { get; set; } } }
State.cs
Create a class file named State.cs within the Models folder and then copy and paste the following code.
namespace InMemoryCachingDemo.Models { public class State { public int StateId { get; set; } public string Name { get; set; } // Foreign key reference to the Country public int CountryId { get; set; } // Navigation property to the parent Country public Country Country { get; set; } // Navigation property to cities belonging to this state public List<City> Cities { get; set; } } }
City.cs
Create a class file named City.cs within the Models folder and then copy and paste the following code.
namespace InMemoryCachingDemo.Models { public class City { public int CityId { get; set; } public string Name { get; set; } // Foreign key reference to the State public int StateId { get; set; } // Navigation property to the parent State public State State { get; set; } } }
Create DbContext
First, Create a new folder named Data in the Project root directory. Then, add a class file named ApplicationDbContext.cs within the Data folder to manage entity configurations and database interaction. Here, we have also inserted some initial seed data using the HasData. Once you create the class, copy and paste the following code.
using InMemoryCachingDemo.Models; using Microsoft.EntityFrameworkCore; namespace InMemoryCachingDemo.Data { public class ApplicationDbContext : DbContext { // Constructor that accepts DbContextOptions and passes it to the base class. public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } // Override OnModelCreating to seed initial data. protected override void OnModelCreating(ModelBuilder modelBuilder) { // Seeding data for Countries modelBuilder.Entity<Country>().HasData( new Country { CountryId = 1, Name = "India" }, new Country { CountryId = 2, Name = "United States" }, new Country { CountryId = 3, Name = "Canada" }, new Country { CountryId = 4, Name = "United Kingdom" } ); // Seeding data for States modelBuilder.Entity<State>().HasData( new State { StateId = 1, Name = "California", CountryId = 2 }, new State { StateId = 2, Name = "Texas", CountryId = 2 }, new State { StateId = 3, Name = "British Columbia", CountryId = 3 }, new State { StateId = 4, Name = "Ontario", CountryId = 3 }, new State { StateId = 5, Name = "England", CountryId = 4 }, new State { StateId = 6, Name = "Maharashtra", CountryId = 1 }, new State { StateId = 7, Name = "Delhi", CountryId = 1 } ); // Seeding data for Cities modelBuilder.Entity<City>().HasData( new City { CityId = 1, Name = "Los Angeles", StateId = 1 }, new City { CityId = 2, Name = "San Francisco", StateId = 1 }, new City { CityId = 3, Name = "Houston", StateId = 2 }, new City { CityId = 4, Name = "Dallas", StateId = 2 }, new City { CityId = 5, Name = "Vancouver", StateId = 3 }, new City { CityId = 6, Name = "Toronto", StateId = 4 }, new City { CityId = 7, Name = "London", StateId = 5 }, new City { CityId = 8, Name = "Mumbai", StateId = 6 }, new City { CityId = 9, Name = "Pune", StateId = 6 } ); } // DbSet properties for each model public DbSet<Country> Countries { get; set; } public DbSet<State> States { get; set; } public DbSet<City> Cities { get; set; } } }
How To Implement In-Memory Caching Work in ASP.NET Core?
In ASP.NET Core, the IMemoryCache interface manages the in-memory cache. To implement In-Memory Caching, we need to follow the following steps:
Inject IMemoryCache:
Register (Add In-Memory Services to the Dependency Injection Container in the Program class) and inject IMemoryCache into our services, controllers, or repositories where we need In-Memory Caching.
Use Cache Methods:
The IMemoryCache interface provides methods to store, retrieve, and manage cached data in the server’s memory. We need to use the following methods:
- Set(key, value, options?): To store or update data in the cache.
- Get(key): To retrieve data from the cache by key.
- TryGetValue(key, out value): To check if data is present and retrieve it if available. That means it tries to get a cache entry by key without throwing an exception if it’s not found.
- Remove(key): To manually remove data from the cache by key.
Define Expiration Policies:
It supports various caching strategies, such as:
- Absolute Expiration: The item remains in the cache until a fixed time passes.
- Sliding Expiration: Reset the expiration timer every time the data is accessed.
- Manual Eviction: The developer explicitly removes cache entries when data changes.
Define Cache Priority:
Cache Priority indicates which items should remain in the cache if the server becomes memory-constrained. When the application is under memory pressure, the runtime first removes lower-priority cache items. Available priorities in ASP.NET Core (CacheItemPriority enum) are:
- Low
- Normal
- High
- NeverRemove
Create a Repository or Service to Implement Basic In-Memory Caching:
To use in-memory caching, we need to inject the IMemoryCache interface into our Controllers, Services, or Repositories. We will create a repository class to demonstrate in-memory caching. First, create a folder named Repository in the Project root directory. Then, add a class file named LocationRepository.cs within the Repository folder and copy and paste the following code. The following code shows how to combine database access and in-memory caching to enhance performance by reducing frequent database queries.
using InMemoryCachingDemo.Data; using InMemoryCachingDemo.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; namespace InMemoryCachingDemo.Repository { public class LocationRepository { // ApplicationDbContext instance for interacting with the database. private readonly ApplicationDbContext _context; // IMemoryCache instance for implementing in-memory caching. private readonly IMemoryCache _cache; // Cache expiration time set to 30 minutes. private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(30); // Constructor to initialize the ApplicationDbContext and IMemoryCache via DI. public LocationRepository(ApplicationDbContext context, IMemoryCache cache) { _context = context; _cache = cache; } // Retrieves the list of countries, with caching. public async Task<List<Country>> GetCountriesAsync() { // Defines a unique key for caching the countries data. var cacheKey = "Countries"; // Tries to get the value of Countries key from cache. if (!_cache.TryGetValue(cacheKey, out List<Country>? countries)) { // If not found in cache, fetch from the database. countries = await _context.Countries .AsNoTracking() // Improves performance for read-only queries .ToListAsync(); // Stores the fetched countries in the cache with a 30-minute expiration. _cache.Set(cacheKey, countries, _cacheExpiration); } // Returns the cached or fresh data. return countries ?? new List<Country>(); } // Retrieves the list of states for a specific country, with caching. public async Task<List<State>> GetStatesAsync(int countryId) { // Unique cache key for states based on country ID. string cacheKey = $"States_{countryId}"; if (!_cache.TryGetValue(cacheKey, out List<State>? states)) { // Fetch from DB if not cached states = await _context.States .Where(s => s.CountryId == countryId) .AsNoTracking() .ToListAsync(); // Cache the result with a 30-minute expiration. _cache.Set(cacheKey, states, _cacheExpiration); } return states ?? new List<State>(); } // Retrieves the list of cities for a specific state, with caching. public async Task<List<City>> GetCitiesAsync(int stateId) { // Unique cache key for cities based on state ID. string cacheKey = $"Cities_{stateId}"; if (!_cache.TryGetValue(cacheKey, out List<City>? cities)) { // Fetch from DB if not cached cities = await _context.Cities .Where(c => c.StateId == stateId) .AsNoTracking() .ToListAsync(); // Cache the result with a 30-minute expiration. _cache.Set(cacheKey, cities, _cacheExpiration); } return cities ?? new List<City>(); } } }
Code Explanation:
- IMemoryCache is injected to store and retrieve data.
- AsNoTracking() boosts performance by disabling EF Core’s change tracking for read-only scenarios.
- If the cache entry doesn’t exist, data is fetched from the database and set into the cache with an expiration.
- Subsequent calls retrieve data directly from the cache until the expiration time is reached (or the cache is cleared).
Creating Controller
Create an Empty API Controller named LocationController within the Controllers folder and then copy and paste the following code. Here, we have created three action methods to access the Countries, States, and cities by Country ID and State ID. The action method uses the Location Repository to fetch the data. The Location Repository uses In-memory caching to reduce database round trips and improve performance.
using InMemoryCachingDemo.Repository; using Microsoft.AspNetCore.Mvc; namespace InMemoryCachingDemo.Controllers { [Route("api/[controller]")] [ApiController] public class LocationController : ControllerBase { private readonly LocationRepository _repository; // The repository is injected via constructor public LocationController(LocationRepository repository) { _repository = repository; } // Retrieves all countries. // GET: api/location/countries [HttpGet("countries")] public async Task<IActionResult> GetCountries() { var countries = await _repository.GetCountriesAsync(); return Ok(countries); } // Retrieves states by country ID. // GET: api/location/states/{countryId} [HttpGet("states/{countryId}")] public async Task<IActionResult> GetStates(int countryId) { var states = await _repository.GetStatesAsync(countryId); return Ok(states); } // Retrieves cities by state ID. // GET: api/location/cities/{stateId} [HttpGet("cities/{stateId}")] public async Task<IActionResult> GetCities(int stateId) { var cities = await _repository.GetCitiesAsync(stateId); return Ok(cities); } } }
Configure Connection String:
In the appsettings.json file, add the connection string so EF Core can connect to your SQL Server. If the database (e.g., LocationDB) does not exist, it will be created. Please modify the appsettings.json file as follows.
{ "ConnectionStrings": { "DefaultConnection": "Server=LAPTOP-6P5NK25R\\SQLSERVER2022DEV;Database=LocationDB;Trusted_Connection=True;TrustServerCertificate=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
Configure the DbContext in the Program.cs Class:
Next, please modify the Program.cs class file as follows to register ApplicationDbContext and enable in-memory caching. This registers the memory caching services with the dependency injection (DI) container. This ensures that dependency injection is available throughout your application.
using InMemoryCachingDemo.Data; using InMemoryCachingDemo.Repository; using Microsoft.EntityFrameworkCore; namespace InMemoryCachingDemo { public class Program { public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Register the ApplicationDbContext with SQL Server connection string builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Register in-memory caching service for caching data in RAM builder.Services.AddMemoryCache(); // Add services to the container. builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); //Register LocationRepository as a Scoped Service builder.Services.AddScoped<LocationRepository>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); } } }
Database Migration:
Open the Package Manager Console, execute the Add-Migration command to create the Migration, and then execute the Update-Database command to apply the migration to the database, as follows.
This will create and apply the migration to our SQL Server, ensuring your database schema matches your model definitions. So, once the above commands are executed, you should see the LocationDB with the Required tables as shown in the below image:
Test Your API:
Use tools like Postman, Fiddler, or Swagger to test the following endpoints:
- GET /api/location/countries
- GET /api/location/states/{countryId}
- GET /api/location/cities/{stateId}
You will notice:
- First request: It takes longer because data is fetched from the database.
- Subsequent requests: Return cached data instantly until it expires.
Cache Eviction Strategies in ASP.NET Core Web API:
Cache Eviction is the process of removing data from the cache when it is no longer valid or necessary. That means Cache Eviction Strategies help manage how and when cached data is removed. The following are the cache eviction strategies in ASP.NET Core Web API:
- Manual Eviction: Explicitly remove items from the cache when data changes, i.e., when an update or deletion happens by calling _cache.Remove().
- Sliding Expiration: The cache expiration time resets every time the data is accessed, so data remains cached as long as it is frequently accessed.
- Absolute Expiration: The cached data expires after a fixed duration, regardless of how often it is accessed.
Example to Understand Cache Eviction Strategies in ASP.NET Core Web API:
Let’s implement the different cache eviction strategies in our example. We will implement the following eviction for different cache data:
- Manual Eviction for Countries
- Sliding Expiration for States
- Absolute Expiration for Cities
To implement Sliding Expiration and Absolute Expiration, we need to create an instance of the MemoryCacheEntryOptions class, set the Expiration strategy accordingly, and pass this instance to the Set method of the Memory Cache instance. To implement Manual Eviction, we don’t need to pass any expiration strategy to the Set method.
For a better understanding, please modify the LocationRepository class as follows to Implement Different Expiration or Eviction Strategies. The following example code is self-explained. Please read the comment lines for a better understanding.
using InMemoryCachingDemo.Data; using InMemoryCachingDemo.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; namespace InMemoryCachingDemo.Repository { public class LocationRepository { private readonly ApplicationDbContext _context; private readonly IMemoryCache _cache; public LocationRepository(ApplicationDbContext context, IMemoryCache cache) { _context = context; _cache = cache; } // 1. Manual Eviction for Countries public async Task<List<Country>> GetCountriesAsync() { var cacheKey = "Countries"; if (!_cache.TryGetValue(cacheKey, out List<Country>? countries)) { countries = await _context.Countries.AsNoTracking().ToListAsync(); // Manual eviction means: do not set any expiration time _cache.Set(cacheKey, countries); // No expiration } return countries ?? new List<Country>(); } // Method to remove countries from the cache manually public void RemoveCountriesFromCache() { var cacheKey = "Countries"; _cache.Remove(cacheKey); } // Add a Country and then clear the cache public async Task AddCountry(Country country) { _context.Countries.Add(country); await _context.SaveChangesAsync(); RemoveCountriesFromCache(); } // Update a Country and then clear the cache public async Task UpdateCountry(Country updatedCountry) { _context.Countries.Update(updatedCountry); await _context.SaveChangesAsync(); RemoveCountriesFromCache(); } // 2. Sliding Expiration for States public async Task<List<State>> GetStatesAsync(int countryId) { string cacheKey = $"States_{countryId}"; if (!_cache.TryGetValue(cacheKey, out List<State>? states)) { states = await _context.States .Where(s => s.CountryId == countryId) .AsNoTracking() .ToListAsync(); // Configure sliding expiration var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(30)); _cache.Set(cacheKey, states, cacheEntryOptions); } return states ?? new List<State>(); } // 3. Absolute Expiration for Cities public async Task<List<City>> GetCitiesAsync(int stateId) { string cacheKey = $"Cities_{stateId}"; if (!_cache.TryGetValue(cacheKey, out List<City>? cities)) { cities = await _context.Cities .Where(c => c.StateId == stateId) .AsNoTracking() .ToListAsync(); // Configure absolute expiration var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)); _cache.Set(cacheKey, cities, cacheEntryOptions); } return cities ?? new List<City>(); } } }
Code Explanation:
- Manual Eviction for Countries: We explicitly clear (Remove) the Countries data after adding or updating a Country.
- Sliding Expiration for States: Every time you access the States cache, its expiration is postponed by the specified timespan, keeping frequently accessed data in the cache.
- Absolute Expiration for Cities: The Cities cache will strictly expire after the given timespan (30 minutes), no matter how many times it’s accessed.
Modifying Controller to Handle Cache Eviction
Please modify the LocationController as follows to add AddCountry and UpdateCountry endpoints. The Framework will automatically handle absolute and Sliding Expiration. We need to handle Manual Eviction explicitly in our code. That means we need to manually clear the Cache when we add or update a country.
using InMemoryCachingDemo.Models; using InMemoryCachingDemo.Repository; using Microsoft.AspNetCore.Mvc; namespace InMemoryCachingDemo.Controllers { [Route("api/[controller]")] [ApiController] public class LocationController : ControllerBase { private readonly LocationRepository _repository; public LocationController(LocationRepository repository) { _repository = repository; } // Retrieves all countries. [HttpGet("countries")] public async Task<IActionResult> GetCountries() { var countries = await _repository.GetCountriesAsync(); return Ok(countries); } // Retrieves states by country ID. [HttpGet("states/{countryId}")] public async Task<IActionResult> GetStates(int countryId) { var states = await _repository.GetStatesAsync(countryId); return Ok(states); } // Retrieves cities by state ID. [HttpGet("cities/{stateId}")] public async Task<IActionResult> GetCities(int stateId) { var cities = await _repository.GetCitiesAsync(stateId); return Ok(cities); } //Add a New Country [HttpPost("countries")] public async Task<IActionResult> AddCountry(Country country) { try { await _repository.AddCountry(country); return Ok(); // Indicates success with No Data to Return } catch (Exception ex) { 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); } } //Update Country By Id [HttpPut("countries/{id}")] public async Task<IActionResult> UpdateCountry(int id, Country country) { if (id != country.CountryId) { return BadRequest(); } try { await _repository.UpdateCountry(country); return Ok(); // Indicates success with No Data to Return } catch (Exception ex) { if (!CountryExists(id)) { return NotFound("Country Does Not Exists"); } 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); } } } private bool CountryExists(int id) { return _repository.GetCountriesAsync().Result.Any(e => e.CountryId == id); } } }
You can now test adding or updating a country. After each operation, the “Countries” cache is manually evicted to ensure subsequent GET requests see the updated data.
Cache Priority in ASP.NET Core Web API
Cache priority determines the relative importance of cache entries. When memory pressure is on the server, items with a lower priority will likely be evicted first. Managing cache priorities ensures that critical data remains cached longer while less important data can be evicted under memory pressure. .NET provides several levels of cache item priority. They are as follows:
- Low: Items that can be discarded easily.
- Normal: The default priority if none is specified.
- High: Items that should be retained longer.
- NeverRemove: Items that should never be automatically removed due to memory pressure.
Example to Understand Cache Priority in ASP.NET Core:
In our LocationRepository, let us set the cache priorities as follows:
- Countries: High priority.
- States: Normal priority.
- Cities: Low priority.
Using the SetPriority method on MemoryCacheEntryOptions and the CacheItemPriority enum, we can set cache priority. So, for a better understanding, please modify the LocationRepository class as follows:
using InMemoryCachingDemo.Data; using InMemoryCachingDemo.Models; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; namespace InMemoryCachingDemo.Repository { public class LocationRepository { private readonly ApplicationDbContext _context; private readonly IMemoryCache _cache; public LocationRepository(ApplicationDbContext context, IMemoryCache cache) { _context = context; _cache = cache; } // Countries: Manual eviction + High Priority public async Task<List<Country>> GetCountriesAsync() { var cacheKey = "Countries"; if (!_cache.TryGetValue(cacheKey, out List<Country>? countries)) { countries = await _context.Countries.AsNoTracking().ToListAsync(); // Set high priority and no expiration var cacheEntryOptions = new MemoryCacheEntryOptions() .SetPriority(CacheItemPriority.High); _cache.Set(cacheKey, countries, cacheEntryOptions); } return countries ?? new List<Country>(); } public void RemoveCountriesFromCache() { var cacheKey = "Countries"; _cache.Remove(cacheKey); } public async Task AddCountry(Country country) { _context.Countries.Add(country); await _context.SaveChangesAsync(); RemoveCountriesFromCache(); } public async Task UpdateCountry(Country updatedCountry) { _context.Countries.Update(updatedCountry); await _context.SaveChangesAsync(); RemoveCountriesFromCache(); } // States: Sliding expiration + Normal priority public async Task<List<State>> GetStatesAsync(int countryId) { string cacheKey = $"States_{countryId}"; if (!_cache.TryGetValue(cacheKey, out List<State>? states)) { states = await _context.States .Where(s => s.CountryId == countryId) .AsNoTracking() .ToListAsync(); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromMinutes(30)) .SetPriority(CacheItemPriority.Normal); _cache.Set(cacheKey, states, cacheEntryOptions); } return states ?? new List<State>(); } // Cities: Absolute expiration + Low priority public async Task<List<City>> GetCitiesAsync(int stateId) { string cacheKey = $"Cities_{stateId}"; if (!_cache.TryGetValue(cacheKey, out List<City>? cities)) { cities = await _context.Cities .Where(c => c.StateId == stateId) .AsNoTracking() .ToListAsync(); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetAbsoluteExpiration(TimeSpan.FromMinutes(30)) .SetPriority(CacheItemPriority.Low); _cache.Set(cacheKey, cities, cacheEntryOptions); } return cities ?? new List<City>(); } } }
When Should We Use In-Memory Caching in ASP.NET Core Web API Real-Time Applications?
- Single-Server: In-memory caching is straightforward and effective if your application runs on a single server.
- Read-Heavy Data: If you have frequently accessed data that changes infrequently (e.g., master data), in-memory caching provides a significant performance boost.
- Limited Data Size: If the dataset to cache is small or moderate, storing it in memory is more cost-effective than repeatedly querying the database.
Important Considerations:
- If you scale out your application to multiple servers, consider a distributed cache (e.g., Redis) to keep cache data consistent across all instances.
- Large data sets in memory can consume excessive server resources and may lead to memory pressure or out-of-memory issues.
In-memory caching is best used for non-volatile, read-heavy data, applications with a single instance, or where slight data inconsistency is acceptable. A distributed caching mechanism is recommended for real-time applications that require consistency across multiple instances or where data changes rapidly.
In the next article, I will discuss Extending the In-Memory Cache in an ASP.NET Core Web API Application with Examples. In this article, I explain How to Implement In-Memory Caching in ASP.NET Core Web API application with Examples. I hope you enjoy this article, How to Implement In-Memory Caching in ASP.NET Core Web API.