I am writing a generic CacheProvider factory which contains a concurrent dictionary to keep the "Named" cache objects (a custom implementation of memory cache based on concurrent dictionary) to be shared among different state machines/pipelines.
I found this article and came up with below implementation
public class CacheProvider<TKey, TType>
{
private Eventing.Tracer tracer;
public delegate TType CreateObjectDelegate();
private readonly ConcurrentDictionary<TKey, CreateObjectDelegate> FactoryMap;
private ConcurrentDictionary<TKey, TType> cachePool;
private static object _lock = new object();
/// <summary>
/// This class is a singleton wrapper on custom memory cache class to be shared with different state machines
/// </summary>
/// <param name="tracer"></param>
public CacheProvider(Eventing.Tracer tracer)
{
Contract.Requires<ArgumentException>(tracer != null, "tracer cannot be null");
this.tracer = tracer;
cachePool = new ConcurrentDictionary<TKey, TType>();
FactoryMap = new ConcurrentDictionary<TKey, CreateObjectDelegate>();
}
public void Register(TKey key, CreateObjectDelegate ctor)
{
// This method registers a delegate to the method used to create the
object so that it can be created later.
//
FactoryMap.TryAdd(key, ctor);
}
public TType CreateOrGetMemoryCache(TKey key)
{
if (cachePool.TryGetValue(key, out TType memoryCache))
{
tracer.Trace(SplitterTraceCodes.CacheProvider, $"MemoryCache with cache key: {key} already created.");
return memoryCache;
}
else
{
tracer.Trace(SplitterTraceCodes.CacheProvider, $"MemoryCache with cache key: {key} not found.");
// Create a new one
CreateObjectDelegate constructor = null;
if (FactoryMap.TryGetValue(key, out constructor))
{
cachePool.TryAdd(key,constructor());
cachePool.TryGetValue(key, out TType memoryCache);
return memoryCache;
}
throw new ArgumentException("Error in Creating MemoryCache. No Type registered for this Cache Key");
}
}
}
TType is as follows:
MyBase
{
string BaseProperty1;
}
MyChild1 : MyBase
{
string ChildProperty1;
}
MyChild2 : MyBase
{
string ChildProperty2;
}
I am looking to setup this at service startup as follows:
CacheProvider<string, object> cacheProvider= new CacheProvider<string, object>();
CacheProvider<string, object>.CreateObjectDelegate createCacheForMyChild1 = new CacheProvider<string, object>.CreateObjectDelegate(CustomConcurrentCache<string,MyChild1>());
CacheProvider<string, object>.CreateObjectDelegate createCacheForMyChild2 = new CacheProvider<string, object>.CreateObjectDelegate(CustomConcurrentCache<string,MyChild2>());
cacheProvider.Register("MyChild1Cache", createCacheForMyChild1 );
cacheProvider.Register("MyChild2Cache", createCacheForMyChild2 );
and use it wherever I need the named cache instance:
CustomConcurrentCache<string,MyChild1> cache = (MyChild1)cacheProvider.CreateOrGetMemoryCache<object>("MyChild1Cache");
Can this be done without using the delegates? Is there any other better way to achieve this?
-
\$\begingroup\$ Hope this is the right place where this question can be answered. Appreciate your help! \$\endgroup\$VJSharp– VJSharp2020年04月20日 20:42:16 +00:00Commented Apr 20, 2020 at 20:42
1 Answer 1
A delegate can be seen as method signature contract. You can define what sort of methods are you willing to work with. For example
- The method should accept type
x
, typey
and typez
as parameters - and should return with type
w
Any method, which satisfy this contract, is a valid method from the delegate perspective.
public delegate TType CreateObjectDelegate();
This delegate says that any method that has no parameter and will return with TType is a good one.
You can describe the same by using the Func
class:
Func<TType> CreateObjectDelegate
Whenever you want to call the Register
method you just have to pass a method which satisfies this contract. For example (let's suppose TType
is int
):
cacheProvider.Register("MyChild1Cache", () => 0);
Func<int> alternative1 = () => 1;
cacheProvider.Register("MyChild2Cache", alternative1.Invoke);
Func<int, Func<int>> alternative2 = i => () => i;
cacheProvider.Register("MyChild3Cache", alternative2.Invoke(2).Invoke);