c# - 缓存先前搜索的策略 (ASP.NET)

标签 c# asp.net caching queue fifo

我有一个 ASP.NET MVC 2 Web 应用程序(.NET 4,C#),用户可以在其中搜索位置

该页面使用自动完成框实现,类似于许多网站。 (谷歌、YouTube 等)

现在,对服务器的 AJAX 调用会导致对数据库的存储过程调用。 (虽然高效,但可能会导致打字慢的人往返很多次)。

我想知道如何创建一个策略来缓存最近 100 次搜索的结果?

我不能使用 OutputCache,因为调用是通过客户端的 AJAX 进行的。我需要缓存存储过程的输出(查询文本的匹配位置列表)。

换句话说,很多人会搜索“纽约”或“旧金山”,而这些数据只能通过手动管理员更改来更改(例如,我们可以手动使缓存无效)。

那么,我如何缓存最近 100 次搜索?我希望有一个类似 FIFO 的功能,如果缓存已经有 100 个搜索,最旧的搜索就会被丢弃,所有的都被向下移动。

我希望代码是这样的:

public ICollection<MatchedLocation> FindLocations(string queryText)
{
    // Check last 100 searches.. How?
    string cacheKey = queryText;
    var matchedLocations = cachedPersistence.Get(cacheKey);

    if (matchedLocations == null)
    {
        // Call db
        matchedLocations = dbPersistence.GetLocations(queryText);

        // Add to cache
        cachedPersistence.Add(cacheKey, matchedLocations);
    }
    else
    {
        // Found in Cache! Awesome!
        return matchedLocations;
    }
}

我认为显而易见的选择是 .NET Queue

但我以前从未使用过它,所以有什么建议吗?我将如何为 get/set 实现并发,我需要使用完全锁定的 Singleton 吗?有没有人为此目的使用过队列?我们还有哪些其他选择?我几乎需要一个自定义队列来限制堆栈中的项目数。

感谢您的帮助。

最佳答案

如果您只想缓存 100,您可以使用带有上次使用时间的对应字典的字典。并且您可以使用读写器锁而不是允许多个读取器的完全成熟的锁。

使用下面的代码,两个线程可能会为相同的值输入 EnterWriteLock。惩罚是两次数据库调用,这可能不是问题。您可以通过执行另一个 TryGetValue 并在必要时锁定(双重锁定)来避免这种情况。

class Cache
{
    static readonly Dictionary<string, ICollection<MatchedLocation>> _cache = new Dictionary<string, ICollection<MatchedLocation>>(100);
    static readonly Dictionary<string,DateTime> _cacheTimes = new Dictionary<string, DateTime>(100);
    static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

    public ICollection<MatchedLocation> FindLocations(string queryText)
    {
        _lock.EnterUpgradeableReadLock();
        try
        {
            ICollection<MatchedLocation> result;
            if (_cache.TryGetValue(queryText, out result))
            {
                return result;
            }
            else
            {
                _lock.EnterWriteLock();
                try
                {
                    // expire cache items
                    if( _cache.Count > 100)
                    {
                        // could be more efficient http://code.google.com/p/morelinq/ - MinBy
                        string key = _cacheTimes.OrderBy(item => item.Value).First().Key;
                        _cacheTimes.Remove(key);
                        _cache.Remove(key);
                    }
                    // add new item
                    result = dbPersistence.GetLocations(queryText);
                    _cache[queryText] = result;
                    _cacheTimes[queryText] = DateTime.UtcNow;                        
                }
                finally
                {
                    _lock.ExitWriteLock();
                }
                return result;
            }
        }
        finally
        {
            _lock.ExitUpgradeableReadLock();
        }
    }
}

关于c# - 缓存先前搜索的策略 (ASP.NET),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3976501/

相关文章:

ruby-on-rails - Rails 3 中虚拟属性的 counter_cache

c# - Windows CE 按钮可以显示图像吗?

c# - 在 C# 中使用泛型理解协变和逆变的问题

javascript - 如何在 DropdownList 更改后刷新页面以便执行 JavaScript

c# - AJAX 返回对象在 asp.net web 表单中包含 d

java - 需要 Java 库——用于远程(例如 S3)文件的本地文件缓存

c# - 创建 C# 绑定(bind)

c# - 如何使用图形突出显示控件中的换行文本?

asp.net - Firefox 中的 Javascript 问题

caching - 过夜预热缓存摘要