using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Collections.Concurrent;
using System.Security.Cryptography;
public static void Main()
var generator = new CacheKeyGenerator();
for(var i = 0; i <1000; i++)
Console.WriteLine($"cache key: {generator.GenerateCacheKey<Program, List<TestThree<TestEnum, Test>>>(9999)}");
Console.WriteLine($"cache key: {generator.GenerateCacheKey<Program, Test>(0)}");
Console.WriteLine($"cache key: {generator.GenerateCacheKey<Program, TestEnum>(0)}");
public class CacheKeyGenerator
private static readonly ConcurrentDictionary<Type, string> ReturnTypePropertyString = new ConcurrentDictionary<Type, string>();
private static readonly ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> TypeProperties = new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>();
public string GenerateCacheKey<TCaller, TReturn>(int dealerId, [CallerMemberName] string memberName = "")
var propertyString = ReturnTypePropertyString.GetOrAdd(typeof(TReturn), CreatePropertyString);
var key = $"(dealer:{dealerId})_(caller:{GetName(typeof(TCaller))}.{memberName})_(return-type:{GetName(typeof(TReturn))})_(property-hash:{propertyString})";
string GetName(Type type)
var nameBuilder = new StringBuilder();
type.GenericTypeArguments.ForEach(t => nameBuilder.Append($"{(position == 0 ? $"{type.Name}<" : string.Empty)}{(position >= 1 ? "," : string.Empty)}T{++position}:{(t.IsGenericType ? GetName(t) : t.FullName)}"));
return nameBuilder.ToString();
public string GenerateGlobalCacheKey<TCaller, TReturn>([CallerMemberName] string memberName = "")
return GenerateCacheKey<TCaller, TReturn>(0, memberName);
private static string CreatePropertyString(Type type)
var properties = new List<PropertyInfo>();
type.GetGenericArguments().ForEach(GetProperties);
var propertyStringBuilder = new StringBuilder();
foreach (var property in properties)
propertyStringBuilder.Append($"({property.Name}:{property.PropertyType.FullName})");
return GetHashFromPropertyString();
void GetProperties(Type typeWithProperties)
var typeProperties = TypeProperties.GetOrAdd(typeWithProperties, t => t.GetProperties());
foreach (var typeProperty in typeProperties)
if (TypeIsNotPrimitiveOrString(typeProperty.PropertyType))
if (typeProperty.PropertyType.IsGenericType)
typeProperty.PropertyType.GetGenericArguments().ForEach(GetProperties);
GetProperties(typeProperty.PropertyType);
properties.Add(typeProperty);
bool TypeIsNotPrimitiveOrString(Type t) => !t.IsPrimitive && t != typeof(string);
string GetHashFromPropertyString()
using (var sha256Hash = SHA256.Create())
var bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(propertyStringBuilder.ToString()));
var builder = new StringBuilder();
foreach (var @byte in bytes)
builder.Append(@byte.ToString("x2"));
return builder.ToString();
public TestTwo TestTwo {get;}
public string MessageTwo {get;}
public class TestThree<T, T2>
public int Property {get;}
public Test PropertyTwo {get;}
public static class Extensions
public static void ForEach<T>(this IEnumerable<T> ie, Action<T> action)