Oct 29 2024
public interface IProductService
{
Product GetProductById(int id);
}
public class ProductService : IProductService
{
public Product GetProductById(int id)
{
// Simulating data retrieval, e.g., from a database
return new Product { Id = id, Name = "Product Name" };
}
}
public class CachedProductService : IProductService
{
private readonly IProductService _innerService;
private readonly IMemoryCache _cache;
public CachedProductService(IProductService innerService, IMemoryCache cache)
{
_innerService = innerService;
_cache = cache;
}
public Product GetProductById(int id)
{
if (!_cache.TryGetValue(id, out Product product))
{
product = _innerService.GetProductById(id);
_cache.Set(id, product);
}
return product;
}
}
public class LoggingProductService : IProductService
{
private readonly IProductService _innerService;
private readonly ILogger<LoggingProductService> _logger;
public LoggingProductService(IProductService innerService, ILogger<LoggingProductService> logger)
{
_innerService = innerService;
_logger = logger;
}
public Product GetProductById(int id)
{
_logger.LogInformation($"Fetching product with ID: {id}");
var product = _innerService.GetProductById(id);
_logger.LogInformation($"Fetched product: {product.Name}");
return product;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddLogging();
// Register the core service
services.AddTransient<IProductService, ProductService>();
// Apply decorators
services.AddTransient<IProductService>(provider =>
{
var productService = provider.GetRequiredService<ProductService>();
var memoryCache = provider.GetRequiredService<IMemoryCache>();
var logger = provider.GetRequiredService<ILogger<LoggingProductService>>();
// First, wrap with caching
var cachedProductService = new CachedProductService(productService, memoryCache);
// Then, wrap with logging
return new LoggingProductService(cachedProductService, logger);
});
}
[ApiController]
[Route("api/[controller]")]
public class ProductController : ControllerBase
{
private readonly IProductService _productService;
public ProductController(IProductService productService)
{
_productService = productService;
}
[HttpGet("{id}")]
public IActionResult GetProduct(int id)
{
var product = _productService.GetProductById(id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
public class RateLimitedProductService : IProductService
{
private readonly IProductService _innerService;
private readonly Dictionary<int, int> _requestCounts = new();
public RateLimitedProductService(IProductService innerService)
{
_innerService = innerService;
}
public Product GetProductById(int id)
{
if (_requestCounts.ContainsKey(id) && _requestCounts[id] >= 5) // Allow 5 requests per product ID
{
throw new InvalidOperationException("Rate limit exceeded for this product.");
}
_requestCounts[id] = _requestCounts.GetValueOrDefault(id, 0) + 1;
return _innerService.GetProductById(id);
}
}
services.AddTransient<IProductService>(provider =>
{
var productService = provider.GetRequiredService<ProductService>();
var memoryCache = provider.GetRequiredService<IMemoryCache>();
var logger = provider.GetRequiredService<ILogger<LoggingProductService>>();
// Layer the decorators
var cachedService = new CachedProductService(productService, memoryCache);
var loggedService = new LoggingProductService(cachedService, logger);
return new RateLimitedProductService(loggedService);
});
Join 13,250+ subscribers to improve your .NET Knowledge.
Go-to resource for understanding the core concepts of design patterns without the overwhelming complexity. In this concise and affordable ebook, I've distilled the essence of design patterns into an easy-to-digest format. It is a Beginner level. Check out it here.
Every Monday morning, I share 1 actionable tip on C#, .NET & Arcitecture topic, that you can use right away.
Subscribe to the TheCodeMan.net and be among the 13,250+ subscribers gaining practical tips and resources to enhance your .NET expertise.