5
\$\begingroup\$

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?

asked Apr 20, 2020 at 20:39
\$\endgroup\$
1
  • \$\begingroup\$ Hope this is the right place where this question can be answered. Appreciate your help! \$\endgroup\$ Commented Apr 20, 2020 at 20:42

1 Answer 1

2
\$\begingroup\$

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, type y and type z 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);
answered May 20, 2020 at 12:53
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.