使用 ConcurrentDictionary 作为缓存并处理更新
Using ConcurrentDictionary as a cache and handling update
我有下面的代码,现在我想添加一个 UpdateSetting
方法。
我能看到的最好的方法是通过 ConcurrentDictionary
上的 TryUpdate
但这意味着知道以前的值所以需要调用 GetSetting
这似乎有点恶心。你觉得呢?你有没有什么想法?有没有更好的方法?
注意:如果值不在缓存中,则什么都不做。成功更新缓存后,它应该调用 settingRepository.Update
谢谢
public class MySettings : IMySettings
{
private readonly ISettingRepository settingRepository;
private readonly ConcurrentDictionary<string, object> cachedValues = new ConcurrentDictionary<string, object>();
public MySettings(ISettingRepository settingRepository)
{
this.settingRepository = settingRepository;
}
public string GetSetting(string key)
{
return this.GetSetting<string>(key);
}
public T GetSetting<T>(string key)
{
object value;
if (!this.cachedValues.TryGetValue(key, out value))
{
value = this.GetValueFromRepository(key, typeof(T));
this.cachedValues.TryAdd(key, value);
}
return (T)value;
}
private object GetValueFromRepository(string key, Type type)
{
var stringValue = this.settingRepository.GetSetting(key);
if (stringValue == null)
{
throw new MissingSettingException(string.Format("A setting with the key '{0}' does not exist.", key));
}
if (type == typeof(string))
{
return stringValue;
}
return ConvertValue(stringValue, type);
}
private static object ConvertValue(string stringValue, Type type)
{
return TypeDescriptor.GetConverter(type).ConvertFromString(stringValue);
}
}
一种方法是简单地设置值,并捕获如果键不在集合中将抛出的异常。
public bool UpdateSetting<T>(string key, T value)
{
try {
this.cachedValues[key] = value;
} catch (KeyNotFoundException ex) {
return false;
}
return true;
}
这是否是您想要处理不存在的密钥的方式取决于您。但是如果你决定要添加密钥,那么你应该使用 AddOrUpdate
方法而不是上面的简单赋值。在这种情况下,您不需要捕获该异常。
为了解决您的后备存储库这个更大的问题,我认为您需要一些与此类似的东西。
public bool UpdateSetting<T>(string key, T value)
{
lock {
try {
this.cachedValues[key] = value;
this.settingRepository.Update(... //you'll have to write this
} catch (KeyNotFoundException ex) {
return false;
}
return true;
}
}
我认为您无法避免使用锁来确保对缓存的更改与存储库匹配。出于同样的原因,我认为您现有的一些代码也可能需要锁定。使用 ConcurrentDictionary 仅保护您对字典的操作。但在更大的范围内,需要同步的事情更多。
可能值得获取现有值以避免更新存储库。如果比 try
更昂贵,则例外
public bool UpdateSetting<T>(string key, T value)
{
lock
{
T oldValue;
if (this.cachedValues.TryGetValue(key, out oldValue)
{
if (oldValue != value)
{
this.cachedValues[key] = value;
settingRepository.Update(key, value);
}
return true;
}
else
{
return false;
}
}
}
我有下面的代码,现在我想添加一个 UpdateSetting
方法。
我能看到的最好的方法是通过 ConcurrentDictionary
上的 TryUpdate
但这意味着知道以前的值所以需要调用 GetSetting
这似乎有点恶心。你觉得呢?你有没有什么想法?有没有更好的方法?
注意:如果值不在缓存中,则什么都不做。成功更新缓存后,它应该调用 settingRepository.Update
谢谢
public class MySettings : IMySettings
{
private readonly ISettingRepository settingRepository;
private readonly ConcurrentDictionary<string, object> cachedValues = new ConcurrentDictionary<string, object>();
public MySettings(ISettingRepository settingRepository)
{
this.settingRepository = settingRepository;
}
public string GetSetting(string key)
{
return this.GetSetting<string>(key);
}
public T GetSetting<T>(string key)
{
object value;
if (!this.cachedValues.TryGetValue(key, out value))
{
value = this.GetValueFromRepository(key, typeof(T));
this.cachedValues.TryAdd(key, value);
}
return (T)value;
}
private object GetValueFromRepository(string key, Type type)
{
var stringValue = this.settingRepository.GetSetting(key);
if (stringValue == null)
{
throw new MissingSettingException(string.Format("A setting with the key '{0}' does not exist.", key));
}
if (type == typeof(string))
{
return stringValue;
}
return ConvertValue(stringValue, type);
}
private static object ConvertValue(string stringValue, Type type)
{
return TypeDescriptor.GetConverter(type).ConvertFromString(stringValue);
}
}
一种方法是简单地设置值,并捕获如果键不在集合中将抛出的异常。
public bool UpdateSetting<T>(string key, T value)
{
try {
this.cachedValues[key] = value;
} catch (KeyNotFoundException ex) {
return false;
}
return true;
}
这是否是您想要处理不存在的密钥的方式取决于您。但是如果你决定要添加密钥,那么你应该使用 AddOrUpdate
方法而不是上面的简单赋值。在这种情况下,您不需要捕获该异常。
为了解决您的后备存储库这个更大的问题,我认为您需要一些与此类似的东西。
public bool UpdateSetting<T>(string key, T value)
{
lock {
try {
this.cachedValues[key] = value;
this.settingRepository.Update(... //you'll have to write this
} catch (KeyNotFoundException ex) {
return false;
}
return true;
}
}
我认为您无法避免使用锁来确保对缓存的更改与存储库匹配。出于同样的原因,我认为您现有的一些代码也可能需要锁定。使用 ConcurrentDictionary 仅保护您对字典的操作。但在更大的范围内,需要同步的事情更多。
可能值得获取现有值以避免更新存储库。如果比 try
更昂贵,则例外public bool UpdateSetting<T>(string key, T value)
{
lock
{
T oldValue;
if (this.cachedValues.TryGetValue(key, out oldValue)
{
if (oldValue != value)
{
this.cachedValues[key] = value;
settingRepository.Update(key, value);
}
return true;
}
else
{
return false;
}
}
}