简体   繁体   English

C#-缓存的延迟加载

[英]C# - Lazy loading with cache

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. 但是我想在单个方法中执行获取缓存锁定的例程,并使用lambda表达式进行调用。

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));
}

I recommend using the Lazy class from .Net, it looks like a match for what you need: 我建议使用.Net中的Lazy类,它看起来像是您所需的匹配项:

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: 您可以将任何lambda设置为Lazy的评估代码,因此只要您在访问器所在的范围内,就可以使用它们来运行初始化代码:

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. 然后在调用代码中使用c.Value

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;
}

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. 在这里使用共享锁并不是很细致。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM