IMemoryCache保证独特的新密钥.NET-Core

时间:2017-02-26 00:30:31

标签: c# asp.net-mvc caching .net-core

我试图使用 Microsoft.Extensions.Caching.Memory.IMemoryCache 接口/类。

我需要在缓存中添加一个新项目,并确保我不会覆盖已保存的任何其他内容。目前,所有密钥都是自动生成和随机化的(不是顺序的)。

如何针对当前缓存项测试随机密钥的唯一性?

或者,如何获得有保证的唯一密钥?可以获得可用于以后检索的自动生成密钥。

https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory有几个例子,但是对于我生成的每个键,使用 .TryGetValue(对象键,对象输出值)来测试唯一性似乎有点矫枉过正,并且在考虑多线程时这可能是一个问题。

博客https://wildermuth.com/2016/04/14/Using-Cache-in-ASP-NET-Core-1-0-RC1对故意密钥使用相同的TryGetValue模式(密钥,输出值)。

我希望不要在其他地方保留这个生成密钥的列表,即另一个列表分隔列表,因为这会让我回到修改列表的问题。

就像额外的一样,在这个特定的情况下,我将传递密钥作为url-querystring参数,因此url兼容的字符串将是超级的。提前谢谢。

3 个答案:

答案 0 :(得分:3)

使用GUID作为缓存密钥并不是一个很好的解决方案,正如您已经发现的那样。主要问题是,在生成GUID之后,无法将其可靠地重新生成到同一个密钥中,以便将数据从缓存中取出。

通常,当我创建缓存时,密钥基于正在缓存的实体或缓存它的方法。但也可以基于值的组合来创建缓存,这些值一起使值唯一。

使用实体的示例

public class Employee
{
    int Id { get; set; }
    string Name { get; set; }
}

要从实体获取密钥,除了实体的主键之外,我们只使用常量值。

private const string KEY_PREFIX = "Employee_";
private object syncLock = new object();

// innerEmployeeRetriever and cache are populated through the constructor
public Employee GetEmployee(int id)
{
    string key = KEY_PREFIX + id.ToString();

    // Get the employee from the cache
    var employee = cache[key];
    if (employee == null)
    {
        lock (syncLock)
        {
            // Double-check that another thread didn't beat us
            // to populating the cache
            var employee = cache[key];
            if (employee == null)
            {
                employee = innerEmployeeRetriever.GetEmployee(id);
                cache[key] = employee;
            }
        }
    }
    return employee;
}

使用方法名称的示例

private object syncLock = new object();

// innerEmployeeRetriever and cache are populated through the constructor
public Employee GetEmployeeList()
{
    string key = "GetEmployeeList";

    // Get the employee from the cache
    var employees = cache[key];
    if (employees == null)
    {
        lock (syncLock)
        {
            // Double-check that another thread didn't beat us
            // to populating the cache
            var employees = cache[key];
            if (employees == null)
            {
                employees = innerEmployeeRetriever.GetEmployeeList();
                cache[key] = employees;
            }
        }
    }
    return employees;
}

使用值组合的示例

您还可以从几个不同的值构建键,这些值组合使实体成为唯一的。如果您没有要使用的主键,或者您要分别缓存多个不同的上下文,这将非常有用。此示例取自MvcSiteMapProvider

protected string GetCacheKey(string memberName)
{
    // NOTE: We must include IsReadOnly in the request cache key 
    // because we may have a different 
    // result when the sitemap is being constructed than when 
    // it is being read by the presentation layer.
    return "__MVCSITEMAPNODE_" + this.SiteMap.CacheKey + "_" + this.Key 
        + "_" + memberName + "_" + this.IsReadOnly.ToString() + "_";
}

在这种情况下,我们基于节点所属的父SiteMap的唯一键,节点的唯一键,方法或属性名称以及当前是否设置只读标志来构建密钥。这些值的每个唯一集合都会生成一个单独的缓存键,为每个组合创建一个单独的缓存。

当然,为了正常工作,应该有某种类型的安全"值之间的分隔符,以防止通过连接不同的值来创建相同的键。例如,"1" + "23""12" + "3"是相同的字符串,但您可以通过使用下划线,竖线字符,逗号或其他一些不会出现在其中的分隔符来阻止这类冲突。数据本身用于分隔值("1" + "_" + "23" "12" + "_" + "3"相同的字符串。)

底线是缓存键必须以某种方式表示缓存中的以使其有用。您的应用程序应该了解如何提供构成密钥的数据,以便在需要再次检索相同数据时重新生成密钥。

答案 1 :(得分:0)

感谢加强Guid独特性的评论:

我在subejct上搜索了一下,发现Eric Lippert的一篇帖子澄清了我的担忧:https://ericlippert.com/2012/05/07/guid-guide-part-three/

  • GUID保证是唯一的,但不保证是随机的。不要将它们用作随机数字。

  • 随机数的GUID不是加密强度随机数。

  • GUID只有在每个人合作时才是唯一的;如果有人想重新使用先前生成的GUID,从而人为地创建碰撞,则无法阻止它们。 GUID不是安全机制。

由于我正在创建自己的密钥,如果我停止删除字符,我可以信任它们。

答案 2 :(得分:0)

你总是可以使用GUID作为唯一键,但理论上它们当然不是唯一的,因为它们的空间有限,但实际上它们赢得的机会可能比陨石少。摧毁地球。因此使用它们相当安全。

另一个问题是你为什么要使用随机ID作为键?当然有一些情况,但通常数据有一些你可以使用的键。例如。如果您在博客引擎中有用户和帖子,则可以始终使用$"user_{user.key}"$"post_{post.key}"或类似的方法来获取您的唯一密钥。您可以使用完整的类名作为替代。如果db中的项具有唯一键,则此方法将起作用。由于你没有完全描述你的情况,很难判断它是否可行,但我宁愿尝试找到自然键""在使用GUID之前。

P.S。小心在URL中以这种方式使用GUID。它可能是也可能不是安全问题。即如果你把所有用户的所有数据都放在缓存中并允许通过URL访问它,那么任何人都可以猜测" guid并访问他们不应该做的事情。这并不容易,但可能这样做,因为你自己写的时候不能保证它们在加密方面足够随机。