【问题标题】:Need help profiling .NET caching extension method需要帮助分析 .NET 缓存扩展方法
【发布时间】:2011-01-03 18:30:58
【问题描述】:

我有以下扩展名

Public Module CacheExtensions
    Sub New()
    End Sub

    Private sync As New Object()
    Public Const DefaultCacheExpiration As Integer = 1200 ''# 20 minutes

    <Extension()> 
    Public Function GetOrStore(Of T)(ByVal cache As Cache, ByVal key As String, ByVal generator As Func(Of T)) As T
        Return cache.GetOrStore(key, If(generator IsNot Nothing, generator(), Nothing), DefaultCacheExpiration)
    End Function

    <Extension()> 
    Public Function GetOrStore(Of T)(ByVal cache As Cache, ByVal key As String, ByVal generator As Func(Of T), ByVal expireInSeconds As Double) As T
        Return cache.GetOrStore(key, If(generator IsNot Nothing, generator(), Nothing), expireInSeconds)
    End Function

    <Extension()> 
    Public Function GetOrStore(Of T)(ByVal cache As Cache, ByVal key As String, ByVal obj As T) As T
        Return cache.GetOrStore(key, obj, DefaultCacheExpiration)
    End Function

    <Extension()> 
    Public Function GetOrStore(Of T)(ByVal cache As Cache, ByVal key As String, ByVal obj As T, ByVal expireInSeconds As Double) As T
        Dim result = cache(key)

        If result Is Nothing Then

            SyncLock sync
                If result Is Nothing Then
                    result = If(obj IsNot Nothing, obj, Nothing)
                    cache.Insert(key, result, Nothing, DateTime.Now.AddSeconds(expireInSeconds), cache.NoSlidingExpiration)
                End If
            End SyncLock
        End If

        Return DirectCast(result, T)

    End Function

End Module

从这里开始,我使用扩展是一个 TagService 来获取标签列表

    Public Function GetTagNames() As List(Of String) Implements Domain.ITagService.GetTags
        ''# We're not using a dynamic Cache key because the list of TagNames
        ''# will persist across all users in all regions.
        Return HttpRuntime.Cache.GetOrStore(Of List(Of String))("TagNamesOnly",
                                                                Function() _TagRepository.Read().Select(Function(t) t.Name).OrderBy(Function(t) t).ToList())
    End Function

除了我在_TagRepository.Read() 上设置断点外,所有这些都非常简单。问题是每次请求都会调用它,而我认为它只会在Result Is Nothing

时调用

我错过了什么吗?

编辑:对于你们 伙计们,这是 C# 等价物

public static class CacheExtensions
{

    private static object sync = new object();
    public const int DefaultCacheExpiration = 20;

    public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator ) {
        return cache.GetOrStore( key, generator != null ? generator() : default( T ), DefaultCacheExpiration );
    }

    public static T GetOrStore<T>( this Cache cache, string key, Func<T> generator, double expireInMinutes ) {
        return cache.GetOrStore( key, generator != null ? generator() : default( T ), expireInMinutes );
    }

    public static T GetOrStore<T>( this Cache cache, string key, T obj ) {
        return cache.GetOrStore( key, obj, DefaultCacheExpiration );
    }

    public static T GetOrStore<T>( this Cache cache, string key, T obj, double expireInMinutes ) {
        var result = cache[key];

        if ( result == null ) {

            lock ( sync ) {
                if ( result == null ) {
                    result = obj != null ? obj : default( T );
                    cache.Insert( key, result, null, DateTime.Now.AddMinutes( expireInMinutes ), Cache.NoSlidingExpiration );
                }
            }
        }

        return (T)result;

    }

}

和电话

    return HttpRuntime.Cache.GetOrStore<List<string>>("TagNamesOnly", () => _TagRepository.Read().Select(t => t.Name).OrderBy(t => t).ToList());

【问题讨论】:

    标签: c# .net caching


    【解决方案1】:

    GetOrStore 的签名及其实现不包含对您发送的 Func 的评估。我真的不知道目前发生了什么(或者它是否真的有效)。您似乎正在将 Func 添加到缓存中。

    public const int DefaultCacheExpiration = 20;
    private static readonly Object SyncRoot = new Object();
    public static T GetOrStore<T>(this Cache cache, String key, Func<T> itemGenerator, Double expireInSeconds = DefaultCacheExpiration) {
        var item = cache[key];
        if (item != null)
            return (T)item;
    
        lock (SyncRoot) {
            // Fetch a second time to check if anyone have
            // added it while we blocked waiting for the lock.
            item = cache[key];
            if (item != null)
                return (T)item;
    
            // Invoke the almighty itemGenerator to execute,
            // and generate, the item that should be inserted
            // into the cache.
            item = itemGenerator.Invoke();
            cache.Insert(key, item, null, DateTime.Now.AddSeconds(expireInSeconds), Cache.NoSlidingExpiration);
            return (T)item;
        }
    }
    
    public static T GetOrStore<T>(this Cache cache, String key, T newItem, Double expireInSeconds = DefaultCacheExpiration) {
        return cache.GetOrStore(key, () => newItem, expireInSeconds);
    }
    

    如果不这样做,请查看您的 asp.net 缓存设置和服务器上的可用内存量。缓存不会在内存不足的情况下工作。

    【讨论】:

    • 我实际上是从stackoverflow-445050 中提取代码
    • 如果我在If result Is Nothing Then 上放一个断点,我可以看到结果 = 正是我需要的结果。
    • 我已经编辑了我的问题以显示整个扩展方法。抱歉之前没说清楚。
    • 您需要等待调用您的生成器,直到您检查缓存不包含您的密钥。您新粘贴的代码包含几个总是调用生成器的重载。
    • 是的,就像我说的,我只是从其他帖子中提取了代码。缓存是我很陌生的一个领域。
    【解决方案2】:

    问题在于您正在调用一个正在调用您的方法的重载方法,并且正如@Simon Svensson 指出的那样,您正在调用的方法不是您发布的方法。这是您实际调用的方法的 C# 版本:

    return cache.GetOrStore( key, generator != null ? generator() : default( T ), DefaultCacheExpiration )
    

    如果第二个参数传递了一个非空值,你应该马上看到第二个参数调用generator()

    更新

    问题与缓存无关,它是您调用的重载方法正在执行您的 lambda 方法。如果您真的打算按照您概述的方式调用该方法,则必须将其更改为以下内容:

    ''# Fix StackOverflow Code Coloring Bug     
    <Extension()>
    Public Function GetOrStore(Of T)(ByVal cache As Cache, ByVal key As String, ByVal generator As Func(Of T)) As T
        ''# Null value to pass to first call. We can not use Nothing because VB can not infer the overload type
        Dim NV As Object = Nothing
        ''# Call the primary method passing a null value
        Dim Ret = cache.GetOrStore(key, NV, DefaultCacheExpiration)
        ''# If that call returns nothing call our generator() method and then re-call the main method
        If (Ret Is Nothing) AndAlso (generator IsNot Nothing) Then
            Ret = cache.GetOrStore(key, generator(), DefaultCacheExpiration)
        End If
        Return Ret
    End Function
    

    我没有对此进行测试,但它或非常接近的东西应该可以满足您的需求。这也是为什么我不是 lambdas 的忠实粉丝。这个想法很棒,但是人们倾向于将这么多的东西混为一谈,并创建出真正不可读的代码。我宁愿有 10 行我可以阅读,而不是 1 行做同样的事情。

    【讨论】:

    • 对不起。缓存绝对是我需要做的事情,因为我几乎迷路了。如果我在GetOrStore 方法中的if ( result == null ) 行上设置断点,我可以看到result 始终填充了正确的数据。所以generator 正在生成正确的调用。只是_TagRepository 每次都会被命中,无论缓存是否存在。至少当我在_TagRepository.Read() 方法上设置断点时,我看到的是这样
    猜你喜欢
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多