Skip to content

Instantly share code, notes, and snippets.

@musoftware
Created October 3, 2025 10:22
Show Gist options
  • Select an option

  • Save musoftware/edc3ecc6ba0e6adffb1d72eafdc1811b to your computer and use it in GitHub Desktop.

Select an option

Save musoftware/edc3ecc6ba0e6adffb1d72eafdc1811b to your computer and use it in GitHub Desktop.
Caching with Attributes (AOP) in C#
// 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;
}
}
// 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);
}
}
// 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