微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

ASP.net缓存访问导致foreach循环中的Collection Modified异常

首先,先做好事.这是支持团队提供的一些异常信息.我知道它发生的行和代码.它发生在对从缓存中获取的字典的FirstOrDefault调用中.

1) Exception information
*********************************************
Exception Type: system.invalidOperationException

Message: Collection was modified; enumeration operation may not execute.

Data: System.Collections.ListDictionaryInternal

现在我想模拟问题,我可以在一个简单的ASP.net应用程序中完成.
我的页面有2个按钮 – Button_Process和Button_Add
背后的代码如下:

public partial class _Default : System.Web.UI.Page
 {
    protected void Page_Load(object sender,EventArgs e)
    {  
       if (!IsPostBack)
        {
            var data = Cache["key"];

            if (data == null)
            {
                var dict = new Dictionary<int,string>();
                for (int i = 0; i < 10; i++)
                {
                    dict.Add(i,"i");
                }

                Cache["key"] = dict;
            }
        }
    }

    protected void ButtonProcess_Click(object sender,EventArgs e)
    {
        var data = Cache["key"] as Dictionary<int,string>;
        if (data != null)
        {
            foreach (var d in data.Values) //In actual code there is FirstOrDefault here
            {
                Thread.Sleep(1000);

                if (d.Contains("5"))
                {
                    //some operation
                }
            }
        }
    }

    protected void Button2_Click(object sender,string>;
        if (data != null)
        {
            data.Add(new Random().Next(),"101");
            Cache["key"] = data;
        }
    }
  }

现在假设有2个请求:

请求1 – 有人点击了button_Process,并且正在对缓存对象进行一些操作
请求2 – 有人点击button_Add并且第一个人获得异常 – 集合修改了等等

我理解这个问题 – 它正在发生,因为我们正在访问相同的内存.我脑子里有两个解决方案:
1.我使用for循环而不是每个循环(在实际代码中替换FirstOrDefault) – 我不知道在进行更改后此操作的效率如何. – 我从来没有从缓存中删除任何项目,所以我在想这个解决方
2.我对这些行上的缓存对象或其他东西进行了一些锁定 – 但我确切地知道在哪里以及如何锁定这个对象.

请帮我解决一下这个.我无法找到有效的解决方案.处理这种情况的最佳方法是什么?

解决方法

发生这种情况是因为您直接使用对象,位于缓存中.良好的做法,避免那些异常和其他奇怪的行为(当你不小心修改缓存对象)正在使用缓存数据的副本.有几种方法可以实现它,比如做克隆或某种深层复制.我更喜欢将对象保存在高速缓存中序列化(您喜欢的任何类型 – json / xml / binary或w / e else),因为(de)序列化会对您的对象进行深层复制.以下小代码片段将澄清一些事情:

public static class CacheManager
{
    private static readonly Cache MyCache = httpruntime.cache;

    public static void Put<T>(T data,string key)
    {
        MyCache.Insert(key,Serialize(data));
    }

    public static T Get<T>(string key)
    {
        var data = MyCache.Get(key) as string;
        if (data != null)
            return Deserialize<T>(data);
        return default(T);
    }

    private static string Serialize(object data)
    {
        //this is Newtonsoft.Json serializer,but you can use the one you like  
        return JsonConvert.SerializeObject(data);
    }

    private static T Deserialize<T>(string data)
    {
        return JsonConvert.DeserializeObject<T>(data);
    }
}

用法

var myObj = new Dictionary<int,int>();
CacheManager.Put(myObj,"myObj");
//...
var anotherObj = CacheManager.Get<Dictionary<int,int>>("myObj");

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐