我怎么能拆离的MemoryCache对象引用 [英] How can I detach the object reference on MemoryCache
问题描述
目前,我正在尝试新的数据的MemoryCache
.NET 4的缓存数位在我们的应用程序之一。我有麻烦的是对象被更新,缓存似乎是持续的变化例如
公开的IEnumerable< SomeObject> GetFromDatabase(){
常量字符串_cacheKeyGetDisplayTree =SomeKey;
ObjectCache _cache = MemoryCache.Default;
VAR objectInCache = _cache.Get(_cacheKeyGetDisplayTree)为IEnumerable< SomeObject取代;
如果(objectInCache!= NULL)
返回objectInCache.ToList();
//做一些事情来获得项目
_cache.Add(_cacheKeyGetDisplayTree,分类,新的DateTimeOffset(DateTime.UtcNow.AddHours(1)));
返回categories.ToList();
}
公开的IEnumerable< SomeObject> GetWithIndentation(){
类变种= GetFromDatabase();
的foreach(按类别变种C)
{
c.Name = - + c.Name;
}
返回类别;
}
如果我分别致电 GetWithIndentation()
第一,然后再调用 GetFromDatabase()
我希望它返回 SomeObject
但相反,它返回修改后的项目(以 - 为前缀的名字)。
我觉得了ToList()
摧毁了参考,但它似乎仍然坚持的变化。我敢肯定,这是显而易见的,但任何人都可以发现我要去哪里错了?
解决方案
我创建了一个ReadonlyMemoryCache类来解决这个
使用系统;
使用System.Collections.Generic;
使用System.Collections.Specialized;
:使用System.IO;
使用System.Runtime.Caching;
使用System.Runtime.Serialization.Formatters.Binary;使用System.Threading.Tasks
;
命名空间ReadOnlyCache
{
类节目
{
静态无效的主要()
{
开始();
到Console.ReadLine();
}
私有静态异步无效的start(){
,而(真)
{
TestMemoryCache();
等待Task.Delay(TimeSpan.FromSeconds(1));
}
}
私有静态无效TestMemoryCache(){
名单,LT;项目>项=无效;
串cacheIdentifier =项目;
VAR缓存= ReadonlyMemoryCache.Default;
//改变的MemoryCache了解问题
// VAR缓存= MemoryCache.Default;
如果(cache.Contains(cacheIdentifier))
{
项= cache.Get(cacheIdentifier)的名单,LT;项目取代;
Console.WriteLine(得到{0}从缓存中的项目:{1},items.Count,的string.join(,项目));
$ B $从缓存中获得B之后//修改,缓存的项目将保持不变
项[0] .value的= DateTime.Now.Millisecond.ToString();
}
如果(项目== NULL)
{
项目=新的List<项目>(){新项目(){值=史蒂夫} ,新的项目(){值=丽莎},新的项目(){值=鲍勃}};
Console.WriteLine(读{0}从磁盘和缓存的项目,items.Count);
//缓存x秒
VAR政策=新CacheItemPolicy(){AbsoluteExpiration =新的DateTimeOffset(DateTime.Now.AddSeconds(5))};
cache.Add(cacheIdentifier,项目,政策);
$ B $写入缓存B之后//修改,缓存的项目将保持不变
项目[1]。价值= DateTime.Now.Millisecond.ToString();
}
}
}
//缓存的项目必须是可序列
[Serializable接口]
类项目{
公共字符串值{获得;组; }
公共重写字符串的ToString(){返回值; }
}
///<总结>
///的MemoryCache的只读版本。对象将始终处于价值被退回,通过深拷贝。
///对象requrements:[Serializable接口],有时有一个反序列化的构造函数(见http://stackoverflow.com/a/5017346/2440)
///< /总结>
公共类ReadonlyMemoryCache:的MemoryCache
{
公共ReadonlyMemoryCache(字符串名称,NameValueCollection中配置= NULL):基地(名称,配置){
}
私有静态ReadonlyMemoryCache高清=新ReadonlyMemoryCache(readonlydefault);
公开新的静态ReadonlyMemoryCache默认{
得到
{
如果(DEF == NULL)
=高清新ReadonlyMemoryCache(readonlydefault);
返回DEF;
}
}
//我们必须在添加时运行deepcopy的,否则项目可以加(后更改),但get()方法
$ B $前b酒店的公共新布尔添加(CacheItem项目,CacheItemPolicy政策)
{
返回base.Add(item.DeepCopy(),政策);
}
公开新对象AddOrGetExisting(字符串键,对象的值,的DateTimeOffset absoluteExpiration,串regionName = NULL)
{
返回base.AddOrGetExisting(键,值。正是deepcopy(),absoluteExpiration,regionName);
}
公开新CacheItem AddOrGetExisting(CacheItem项目,CacheItemPolicy政策)
{
返回base.AddOrGetExisting(item.DeepCopy(),政策);
}
公开新对象AddOrGetExisting(字符串键,对象的值,CacheItemPolicy政策,串regionName = NULL)
{
返回base.AddOrGetExisting(键,值。正是deepcopy(),政策,regionName);
}
//从ObjectCache
公布尔新添加方法(字符串键,对象的值,的DateTimeOffset absoluteExpiration,串regionName = NULL)
{
返回base.Add(键,value.DeepCopy(),absoluteExpiration,regionName);
}
公布尔新添加(字符串键,对象的值,CacheItemPolicy政策,串regionName = NULL)
{
返回base.Add(键,值。正是deepcopy(),政策,regionName);
}
//不知什么原因,我们还需要抵达数值时deepcopy的,尽管我们在所有(?)set方法运行deepcopy的。
公开新对象获取(字符串键,字符串regionName = NULL)
{
VAR项目= base.Get(键,regionName);
返回item.DeepCopy();
}
公开新CacheItem GetCacheItem(字符串键,字符串regionName = NULL)
{
VAR项目= base.GetCacheItem(键,regionName);
返回item.DeepCopy();
}
}
公共静态类DeepCopyExtentionMethods
{
///<总结>
///创建对象的深层副本。必须是[Serializable接口],有时有一个反序列化的构造函数(见http://stackoverflow.com/a/5017346/2440)
///< /总结>
公共静态牛逼deepcopy的< T>(这件T OBJ)
{使用
(VAR毫秒=新的MemoryStream())
{
变种格式化=新的BinaryFormatter( );
formatter.Serialize(MS,OBJ);
ms.Position = 0;
回报率(T)formatter.Deserialize(MS);
}
}
}
}
I'm currently trying out the new MemoryCache
in .Net 4 to cache a few bits of data in one of our apps. The trouble I have is the objects are updated and the cache appears to be persisting the changes e.g.
public IEnumerable<SomeObject> GetFromDatabase(){
const string _cacheKeyGetDisplayTree = "SomeKey";
ObjectCache _cache = MemoryCache.Default;
var objectInCache = _cache.Get(_cacheKeyGetDisplayTree) as IEnumerable<SomeObject>;
if (objectInCache != null)
return objectInCache.ToList();
// Do something to get the items
_cache.Add(_cacheKeyGetDisplayTree, categories, new DateTimeOffset(DateTime.UtcNow.AddHours(1)));
return categories.ToList();
}
public IEnumerable<SomeObject> GetWithIndentation(){
var categories = GetFromDatabase();
foreach (var c in categories)
{
c.Name = "-" + c.Name;
}
return categories;
}
If I were calling GetWithIndentation()
first and then later calling GetFromDatabase()
I would expect it to return the original list of SomeObject
but instead it returns the modified items (with "-" prefixed on the name).
I thought ToList()
destroyed the reference but it still seems to persist the changes. I'm sure it's obvious but can anyone spot where I'm going wrong?
I created a ReadonlyMemoryCache class to solve this problem. It inherits from the .NET 4.0 MemoryCache, but objects are stored readonly (by-value) and cannot be modified. I deep copy the objects before storing using binary serialization.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Runtime.Caching;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading.Tasks;
namespace ReadOnlyCache
{
class Program
{
static void Main()
{
Start();
Console.ReadLine();
}
private static async void Start() {
while (true)
{
TestMemoryCache();
await Task.Delay(TimeSpan.FromSeconds(1));
}
}
private static void TestMemoryCache() {
List<Item> items = null;
string cacheIdentifier = "items";
var cache = ReadonlyMemoryCache.Default;
//change to MemoryCache to understand the problem
//var cache = MemoryCache.Default;
if (cache.Contains(cacheIdentifier))
{
items = cache.Get(cacheIdentifier) as List<Item>;
Console.WriteLine("Got {0} items from cache: {1}", items.Count, string.Join(", ", items));
//modify after getting from cache, cached items will remain unchanged
items[0].Value = DateTime.Now.Millisecond.ToString();
}
if (items == null)
{
items = new List<Item>() { new Item() { Value = "Steve" }, new Item() { Value = "Lisa" }, new Item() { Value = "Bob" } };
Console.WriteLine("Reading {0} items from disk and caching", items.Count);
//cache for x seconds
var policy = new CacheItemPolicy() { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddSeconds(5)) };
cache.Add(cacheIdentifier, items, policy);
//modify after writing to cache, cached items will remain unchanged
items[1].Value = DateTime.Now.Millisecond.ToString();
}
}
}
//cached items must be serializable
[Serializable]
class Item {
public string Value { get; set; }
public override string ToString() { return Value; }
}
/// <summary>
/// Readonly version of MemoryCache. Objects will always be returned in-value, via a deep copy.
/// Objects requrements: [Serializable] and sometimes have a deserialization constructor (see http://stackoverflow.com/a/5017346/2440)
/// </summary>
public class ReadonlyMemoryCache : MemoryCache
{
public ReadonlyMemoryCache(string name, NameValueCollection config = null) : base(name, config) {
}
private static ReadonlyMemoryCache def = new ReadonlyMemoryCache("readonlydefault");
public new static ReadonlyMemoryCache Default {
get
{
if (def == null)
def = new ReadonlyMemoryCache("readonlydefault");
return def;
}
}
//we must run deepcopy when adding, otherwise items can be changed after the add() but before the get()
public new bool Add(CacheItem item, CacheItemPolicy policy)
{
return base.Add(item.DeepCopy(), policy);
}
public new object AddOrGetExisting(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
{
return base.AddOrGetExisting(key, value.DeepCopy(), absoluteExpiration, regionName);
}
public new CacheItem AddOrGetExisting(CacheItem item, CacheItemPolicy policy)
{
return base.AddOrGetExisting(item.DeepCopy(), policy);
}
public new object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)
{
return base.AddOrGetExisting(key, value.DeepCopy(), policy, regionName);
}
//methods from ObjectCache
public new bool Add(string key, object value, DateTimeOffset absoluteExpiration, string regionName = null)
{
return base.Add(key, value.DeepCopy(), absoluteExpiration, regionName);
}
public new bool Add(string key, object value, CacheItemPolicy policy, string regionName = null)
{
return base.Add(key, value.DeepCopy(), policy, regionName);
}
//for unknown reasons, we also need deepcopy when GETTING values, even though we run deepcopy on all (??) set methods.
public new object Get(string key, string regionName = null)
{
var item = base.Get(key, regionName);
return item.DeepCopy();
}
public new CacheItem GetCacheItem(string key, string regionName = null)
{
var item = base.GetCacheItem(key, regionName);
return item.DeepCopy();
}
}
public static class DeepCopyExtentionMethods
{
/// <summary>
/// Creates a deep copy of an object. Must be [Serializable] and sometimes have a deserialization constructor (see http://stackoverflow.com/a/5017346/2440)
/// </summary>
public static T DeepCopy<T>(this T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
}
}
这篇关于我怎么能拆离的MemoryCache对象引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!