2

I have a repository decorator. This decorator is responsible of the caching of the decorated repository. In most of my functions of this decorator I just return the result for the cache if exist or call the method on the decorated repository and store this result in the cache if not yet in this cache. I do that thead safe.

But I would like to do this routine of getting the cache lock, ... in a single method and call it with a lambda expression.

My method to get the result for the cache or load it:

private X CallCachedAndLocked<X>(string methodCacheKey, xxx methodToCallWithParameter)
{
    var cacheKey = GetCacheKey(methodCacheKey);
    X obj = (X)Cache.Get(cacheKey);
    if (obj == null)
   {
        lock (getLastDrawResult_lock)
        {
            if (obj == null)
            {
                obj = methodToCallWithParameter;
                if (obj != null)
                {
                    Cache.Add(cacheKey,
                        obj,
                        null,
                        NextExpiration,
                        System.Web.Caching.Cache.NoSlidingExpiration,
                        CacheItemPriority.AboveNormal, null);
                }
            }
        }
    }
}

Examples of calls:

public T GetDraw(int id)
{
    return CallCachedAndLocked<T>(() => _decoratedRepository.GetDraw(id));
}        

public IEnumerable<T> GetDraws(DateTime from)
{
    return CallCachedAndLocked<T>(() => _decoratedRepository.GetDraws(GetDraws));
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Kapoue
  • 847
  • 2
  • 11
  • 15

3 Answers3

2

I recommend using the Lazy class from .Net, it looks like a match for what you need:

var lazyCall = new Lazy<T>(() => new T());

and when accessing the value

lazyCall.Value // launches evaluation of the lambda expression


You can set any lambda as the evaluation code for the Lazy, so as long as you are in a scope where your accessors exist, you can use them to run the initialization code:

var c = MyCache.Get[key]();
if (c == null)
{
    c = methodToCallWithParameter(key);
    MyCache.Add(key, c);
}
return c;

is more or less equivalent to:

c = new Lazy<cType>(() => methodToCallWithParameter(key));
return c;

and then using c.Value in the calling code.

samy
  • 14,832
  • 2
  • 54
  • 82
  • I don't think that it will do the trick here for 2 reasons: - I have to pass some parameters and call a method on my repository - It is not cached – Kapoue Sep 29 '14 at 12:59
  • Then just call the method during the declaration of the Lazy. SEe edit – samy Sep 29 '14 at 13:02
  • I don't need to pass key to my method to call and how do you define the parameter methodToCallWithParameter in my general function? – Kapoue Sep 29 '14 at 13:28
0

I have finally found a solution with the reflection. I don't see a solution without it:

public static DateTime GetCachedMethod(int nbMonth, NonDecoratedClass repo)
{
    var methodCacheKey = "Test";
    DateTime obj = new DateTime();

    if (!cache.ContainsKey(methodCacheKey))
    {
        lock (zeLock)
        {
            if (!cache.ContainsKey(methodCacheKey))
            {
                obj = repo.GetDateTimeAndMonth(nbMonth);
                if (obj != null)
                {
                    cache.Add(methodCacheKey, obj);
                }
            }
        }
    }
    else
    {
        obj = (DateTime)cache[methodCacheKey];

    }
    return obj;
}
Mehdi Dehghani
  • 10,970
  • 6
  • 59
  • 64
Kapoue
  • 847
  • 2
  • 11
  • 15
-1

You can easily do this with an extension method along the lines of:

private static readonly _lock = new object();

public static void Lock(Action action)
{
    // Lock.
    lock (_lock) action();
}

public static T Lock<T>(Func<T> func)
{
    // Lock.
    lock (_lock) return func();
}

However, you really shouldn't do this; you're sharing the same lock for everything, and that's just going to lead to contention.

You want your locks to be as granular as possible so as to not tie up other waiting threads while locking. Using a shared lock here is anything but granular.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • You are right about the lock I will use a dictionary of locks then. But it doesn't resolve my problem because I need to have multiple signatures of my methods to call one time it is one int, the other time it is 2 dates, ... – Kapoue Sep 29 '14 at 13:04
  • That's the point I'm trying to make, you're gaining nothing by having a dictionary of locks (what's the key here, the `Func`? That won't work, as it's a reference). You want to keep the locking code associated with the calls to `GetDraw`, `GetDraws` etc. where you can coordinate the locks with other locks to achieve the granularity you want. – casperOne Sep 29 '14 at 18:38