我试图使用 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兼容的字符串将是超级的。提前谢谢。
答案 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并访问他们不应该做的事情。这并不容易,但可能这样做,因为你自己写的时候不能保证它们在加密方面足够随机。