Para começar, deixe-me jogar por aí que sei que o código abaixo não é seguro para threads (correção: pode ser). O que estou lutando é encontrar uma implementação que seja e que eu possa realmente falhar no teste. Estou refatorando um grande projeto WCF agora que precisa de alguns (principalmente) dados estáticos armazenados em cache e preenchidos a partir de um banco de dados SQL. Ele precisa expirar e "atualizar" pelo menos uma vez por dia, por isso estou usando o MemoryCache.
Eu sei que o código abaixo não deve ser thread-safe, mas não consigo fazê-lo falhar sob carga pesada e complicar as coisas, uma pesquisa do Google mostra implementações em ambos os sentidos (com e sem bloqueios combinados com debates se eles são ou não necessários.
Alguém com conhecimento de MemoryCache em um ambiente multiencadeado poderia me informar definitivamente se preciso ou não bloquear onde apropriado para que uma chamada para remover (que raramente será chamada, mas é um requisito) não será lançada durante a recuperação / repopulação.
public class MemoryCacheService : IMemoryCacheService
{
private const string PunctuationMapCacheKey = "punctuationMaps";
private static readonly ObjectCache Cache;
private readonly IAdoNet _adoNet;
static MemoryCacheService()
{
Cache = MemoryCache.Default;
}
public MemoryCacheService(IAdoNet adoNet)
{
_adoNet = adoNet;
}
public void ClearPunctuationMaps()
{
Cache.Remove(PunctuationMapCacheKey);
}
public IEnumerable GetPunctuationMaps()
{
if (Cache.Contains(PunctuationMapCacheKey))
{
return (IEnumerable) Cache.Get(PunctuationMapCacheKey);
}
var punctuationMaps = GetPunctuationMappings();
if (punctuationMaps == null)
{
throw new ApplicationException("Unable to retrieve punctuation mappings from the database.");
}
if (punctuationMaps.Cast<IPunctuationMapDto>().Any(p => p.UntaggedValue == null || p.TaggedValue == null))
{
throw new ApplicationException("Null values detected in Untagged or Tagged punctuation mappings.");
}
// Store data in the cache
var cacheItemPolicy = new CacheItemPolicy
{
AbsoluteExpiration = DateTime.Now.AddDays(1.0)
};
Cache.AddOrGetExisting(PunctuationMapCacheKey, punctuationMaps, cacheItemPolicy);
return punctuationMaps;
}
//Go oldschool ADO.NET to break the dependency on the entity framework and need to inject the database handler to populate cache
private IEnumerable GetPunctuationMappings()
{
var table = _adoNet.ExecuteSelectCommand("SELECT [id], [TaggedValue],[UntaggedValue] FROM [dbo].[PunctuationMapper]", CommandType.Text);
if (table != null && table.Rows.Count != 0)
{
return AutoMapper.Mapper.DynamicMap<IDataReader, IEnumerable<PunctuationMapDto>>(table.CreateDataReader());
}
return null;
}
}