我怎么能拆离的MemoryCache对象引用 [英] How can I detach the object reference on MemoryCache

查看:165
本文介绍了我怎么能拆离的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类来解决这个

问题。它从.NET 4.0的MemoryCache继承,但对象只读存储(按值),并且不能修改。我深深的使用二进制序列存放之前,对象复制

 使用系统; 
使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆