Created
October 3, 2025 10:22
-
-
Save musoftware/edc3ecc6ba0e6adffb1d72eafdc1811b to your computer and use it in GitHub Desktop.
Caching with Attributes (AOP) in C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Filename: CachingProxy.cs | |
| using System; | |
| using System.Collections.Concurrent; | |
| using System.Reflection; | |
| using System.Text.Json; | |
| public class CachingProxy : DispatchProxy | |
| { | |
| private object _wrappedObject; | |
| private static readonly ConcurrentDictionary<string, (object Value, DateTime Expiration)> _cache = new(); | |
| protected override object Invoke(MethodInfo targetMethod, object[] args) | |
| { | |
| var cacheAttribute = targetMethod.GetCustomAttribute<TTLCacheAttribute>(); | |
| // If the method doesn't have our attribute, just call it directly. | |
| if (cacheAttribute == null) | |
| { | |
| return targetMethod.Invoke(_wrappedObject, args); | |
| } | |
| // Create a unique key based on the method name and its arguments. | |
| string cacheKey = $"{targetMethod.Name}:{JsonSerializer.Serialize(args)}"; | |
| // Check the cache. | |
| if (_cache.TryGetValue(cacheKey, out var item) && item.Expiration > DateTime.UtcNow) | |
| { | |
| Console.WriteLine($"CACHE HIT for key: '{cacheKey}'"); | |
| return item.Value; | |
| } | |
| Console.WriteLine($"CACHE MISS or EXPIRED for key: '{cacheKey}'. Executing method."); | |
| // Execute the actual method. | |
| object result = targetMethod.Invoke(_wrappedObject, args); | |
| // Store the result in the cache. | |
| var expirationTime = DateTime.UtcNow.Add(cacheAttribute.TTL); | |
| _cache[cacheKey] = (result, expirationTime); | |
| return result; | |
| } | |
| public static T Create<T, TImpl>() where T : class where TImpl : T, new() | |
| { | |
| var proxy = Create<T, CachingProxy>() as CachingProxy; | |
| proxy._wrappedObject = new TImpl(); | |
| return proxy as T; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Filename: TTLCacheAttribute.cs | |
| using System; | |
| [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] | |
| public sealed class TTLCacheAttribute : Attribute | |
| { | |
| public TimeSpan TTL { get; } | |
| public TTLCacheAttribute(int ttlSeconds) | |
| { | |
| TTL = TimeSpan.FromSeconds(ttlSeconds); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Filename: WeatherService.cs | |
| using System; | |
| using System.Threading; | |
| // The interface that our proxy will implement. | |
| public interface IWeatherService | |
| { | |
| string GetCurrentWeather(string city); | |
| } | |
| // The concrete implementation of our service. | |
| public class WeatherService : IWeatherService | |
| { | |
| [TTLCache(5)] // Cache the result of this method for 5 seconds. | |
| public string GetCurrentWeather(string city) | |
| { | |
| Console.WriteLine($"--- Making a slow API call for {city}... ---"); | |
| Thread.Sleep(2000); // Simulate network latency. | |
| return $"Weather for {city} as of {DateTime.Now:T}: 28°C, Clear Skies"; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment