2
Part -2

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
public class CryptoController : Controller
{
private readonly IMemoryCache _memoryCache;
private readonly ICryptoService _cryptoService;
public CryptoController(IMemoryCache memoryCache, ICryptoService cryptoService)
{
_memoryCache = memoryCache;
_cryptoService = cryptoService;
}
// IMemoryCache Example: Caching specific data objects
public async Task<IActionResult> GetCryptoPrices(string cryptocurrency)
{
// Create a unique cache key
string cacheKey = $"crypto_price_{cryptocurrency}";
// Try to get the value from the cache
if (!_memoryCache.TryGetValue(cacheKey, out CryptoPrice cryptoPrice))
{
// If not in cache, fetch from service
cryptoPrice = await _cryptoService.GetLatestPriceAsync(cryptocurrency);
// Set cache options
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(10))
.SetAbsoluteExpiration(TimeSpan.FromHours(1));
// Save data in cache
_memoryCache.Set(cacheKey, cryptoPrice, cacheEntryOptions);
}
return Ok(cryptoPrice);
}
// OutputCache Example: Caching entire HTTP response
[OutputCache(Duration = 300, Location = OutputCacheLocation.Any)]
public async Task<IActionResult> GetMarketSummary()
{
// This entire method result will be cached for 5 minutes
var marketSummary = await _cryptoService.GetMarketSummaryAsync();
return Ok(marketSummary);
}
// Separate method to demonstrate cache invalidation
public async Task InvalidateCryptoCache(string cryptocurrency)
{
string cacheKey = $"crypto_price_{cryptocurrency}";
_memoryCache.Remove(cacheKey);
}
}
// Supporting interfaces and models
public interface ICryptoService
{
Task<CryptoPrice> GetLatestPriceAsync(string cryptocurrency);
Task<MarketSummary> GetMarketSummaryAsync();
}
public class CryptoPrice
{
public string Cryptocurrency { get; set; }
public decimal CurrentPrice { get; set; }
public DateTime Timestamp { get; set; }
}
public class MarketSummary
{
public decimal TotalMarketCap { get; set; }
public decimal TotalVolume { get; set; }
public List<CryptoPrice> TopCryptoPrices { get; set; }
}
- IMemoryCache:
- Used for
GetCryptoPrices()
- Caches individual cryptocurrency price objects
- Allows fine-grained control over caching
- Can be manually invalidated
- Supports sliding and absolute expiration
- OutputCache Attribute:
- Used for
GetMarketSummary()
- Caches entire HTTP response
- Simpler implementation
- Fixed duration cache
- Automatically handles response caching
In the current implementation I have used both the both IMemoryCache and OutputCache and see below for more highlights as listed. Hope this helps
- Separate caching strategies for different use cases
- Dependency injection of
IMemoryCache
- Flexible caching with options for sliding and absolute expiration
- Method-level caching with
OutputCache


2
IMemoryCache
- In-memory caching mechanism in .NET Core
- Stores objects in application's memory
- Best for server-side caching of small to medium-sized objects
- Supports key-based retrieval of cached items
- Provides fine-grained control over caching
- Useful for database query results, computed values, API responses
OutputCache Attribute
- Caches entire HTTP responses
- Primarily used for caching entire page or action method outputs
- Handles server-side page/action result caching
- Works at the HTTP response level
- Simplifies caching of complete web page or API endpoint responses
// IMemoryCache Example
public class ProductService
{
private readonly IMemoryCache _memoryCache;
public async Task<Product> GetProductAsync(int productId)
{
// Check cache first
if (!_memoryCache.TryGetValue(productId, out Product product))
{
// Fetch from database if not in cache
product = await _repository.GetProductByIdAsync(productId);
// Cache the result with sliding expiration
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
_memoryCache.Set(productId, product, cacheEntryOptions);
}
return product;
}
}
// OutputCache Attribute Example
public class ProductController : Controller
{
[OutputCache(Duration = 300, Location = OutputCacheLocation.Any)]
public async Task<IActionResult> GetProductDetails(int productId)
{
var product = await _productService.GetProductAsync(productId);
return View(product);
}
}
As an example I was working on this example for a PoC application on Blockchain exchange application.


1
Difference Between IMemoryCache and OutputCache
Aspect
|
IMemoryCache
|
OutputCache
|
Purpose
|
Caches arbitrary data in memory, used for custom caching logic.
|
Caches the output of a controller action or page to reduce processing time.
|
Scope
|
Application-wide caching (manual control).
|
Response/output-level caching (controller or action level).
|
Flexibility
|
Developer-defined keys and values require explicit implementation.
|
Framework-managed, minimal configuration needed.
|
Caching Level
|
Any data object, such as strings, collections, or complex objects.
|
Entire HTTP response/output for a specific endpoint.
|
Expiration
|
Fully customisable (e.g., time-based or size-based eviction).
|
Time-based expiration or conditional logic via attributes.
|
Use Case
|
Caching shared data, like configuration, query results, or API responses.
|
Caching the rendered result of a web page or API action to reduce server load.
|
1. IMemoryCache
IMemoryCache is a service that stores and retrieves arbitrary data in memory. You control what data to cache, when, and how to manage expiration.
Example Use Case:
Caching the result of a database query to avoid repetitive calls.
public class ProductService
{
private readonly IMemoryCache _memoryCache;
public ProductService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public async Task<List<Product>> GetProductsAsync()
{
// Check if data is already cached
if (!_memoryCache.TryGetValue("ProductCacheKey", out List<Product> products))
{
// Simulate database call
products = await GetProductsFromDatabaseAsync();
// Cache the data for 5 minutes
_memoryCache.Set("ProductCacheKey", products, TimeSpan.FromMinutes(5));
}
return products;
}
private Task<List<Product>> GetProductsFromDatabaseAsync()
{
return Task.FromResult(new List<Product>
{
new Product { Id = 1, Name = "Product A" },
new Product { Id = 2, Name = "Product B" }
});
}
}
When to Use IMemoryCache:
- Caching shared data across the application (e.g., lookup tables, configurations).
- Caching results of expensive computations or database calls.
2. OutputCache
OutputCache is specifically designed to cache the output (e.g., HTML or JSON) of an API endpoint or controller action. It reduces processing time for frequently accessed pages or API responses.
Example Use Case:
Caching the response of a product listing API for 1 minute.
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// Cache the entire response for 1 minute
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
[HttpGet]
public IActionResult GetProducts()
{
var products = new List<Product>
{
new Product { Id = 1, Name = "Product A" },
new Product { Id = 2, Name = "Product B" }
};
return Ok(products);
}
}
When to Use OutputCache:
- Caching entire page responses or API responses.
- Reducing processing overhead for frequently accessed endpoints.
Live Example Comparison
Scenario: A product listing page.
- IMemoryCache:
- Cache the product data retrieved from the database.
- Useful when multiple endpoints or views need the same data.
Example:
-
- Controller action retrieves products from IMemoryCache and then formats the output dynamically.
- OutputCache:
- Cache the entire rendered output of the product listing API or page.
- Useful for static or rarely changing responses.
Example:
-
- The client hits the API for /api/products and gets a cached JSON response without reprocessing.
Summary
- Use IMemoryCache for data-level caching (e.g., query results, computed values).
- Use OutputCache for output-level caching (e.g., rendered HTML or API responses).

1
In .NET Core, IMemoryCache
and the OutputCache
attribute serve different purposes and are used in distinct scenarios to improve application performance. Here's a breakdown of their differences and when to use each:
1. IMemoryCache
Definition
IMemoryCache
is part of the in-memory caching mechanism provided by .NET Core for general-purpose data caching.
- It allows you to store and retrieve data in memory for quick access.
Use Case
- When you want to cache arbitrary data, such as:
- API responses
- Computed data
- Frequently used configurations
- Query results
- The cache is programmatically controlled, giving you full flexibility to set keys, expiration times, and eviction policies.
Features
- Programmatic Control: You have direct control over what is cached and how long it stays cached.
- Flexible Storage: Cache anything that can be serialized, such as objects, strings, or collections.
- Eviction Policies: Supports eviction based on time-to-live (absolute/relative expiration) or memory pressure.
- Thread-Safe: Designed to work efficiently in concurrent environments.
Example
public class DataService
{
private readonly IMemoryCache _memoryCache;
public DataService(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public string GetCachedData()
{
return _memoryCache.GetOrCreate("cacheKey", entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
return "This is cached data";
});
}
}
When to Use
- When caching application-level data for reuse across different requests or sessions.
- For fine-grained caching control, particularly when the data is not tied to a specific HTTP response.
2. OutputCache Attribute
Definition
OutputCache
is used to cache the entire HTTP response generated by a controller action or Razor page.
- It is part of the response caching mechanism in ASP.NET Core, optimizing the delivery of static or semi-static content.
Use Case
- When you want to cache the full HTTP response to reduce server processing for repeated requests.
- Ideal for scenarios where the response is deterministic and doesn't vary based on user-specific data (e.g., public API responses, static pages).
Features
- Automatic Response Caching: Caches the HTTP response with minimal code.
- Declarative: Applied at the action or controller level using attributes.
- Fine-tuned Configuration: Allows setting policies such as duration, vary-by-header, and vary-by-query-parameters.
Example
[OutputCache(Duration = 60)]
public IActionResult Get()
{
return Ok("This response is cached for 60 seconds");
}
When to Use
- When you want to cache the entire HTTP response to serve identical requests faster.
- Suitable for read-heavy endpoints with consistent responses.
Key Differences
Aspect |
IMemoryCache |
OutputCache |
Scope |
Caches arbitrary data programmatically. |
Caches full HTTP responses automatically. |
Granularity |
Fine-grained (data-level caching). |
Coarse-grained (response-level caching). |
Use Case |
Cache data like API results, configurations. |
Cache entire HTTP responses for faster delivery. |
Configuration |
Fully controlled in code. |
Configured via attributes or middleware. |
Applicability |
Suitable for caching reusable data. |
Suitable for caching idempotent responses. |
Integration |
Explicitly managed in application code. |
Declaratively managed via attributes. |
Choosing Between IMemoryCache and OutputCache
-
Use IMemoryCache
when:
- You need to cache reusable data or objects for use across various parts of your application.
- The data is independent of specific HTTP responses.
- You require granular control over caching logic and expiration policies.
-
Use OutputCache
when:
- You want to cache the entire HTTP response for a particular endpoint.
- Your endpoint returns static or semi-static content.
- The response doesn't depend on user-specific data or session state.
By leveraging the right caching mechanism, you can optimize the performance and responsiveness of your .NET Core application effectively.
