65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 运行时缓存 - 克隆对象以保留缓存数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (5投票s)

2009年2月13日

CPOL

3分钟阅读

viewsIcon

41171

一篇关于如果要在.NET缓存中保留缓存数据,对象克隆的重要性的文章

引言

让我们想象一下,您已经在内存中填充了一个对象,然后将该对象写入ASP.NET运行时缓存。您知道,当您从缓存中读取您的对象时...

MyClass myObject = ( MyClass )HttpRuntime.Cache[ "mykey" ];
图 1

...您将拥有对缓存可以访问的同一对象实例的引用?这意味着您在对象(图2)中所做的任何更改都将被未来对同一缓存对象的所有请求看到。

// no so...  I'm afraid every future request will also see this description
myObject.Description = "My very own description"; 
图 2

现在,这不一定是件好事。事实上,这对您来说可能非常糟糕,因为也许,您想缓存一个对象,然后保留该对象直到它从缓存中过期。与此同时,对这个缓存对象的新请求可能希望对其进行小的修改以供自己使用,因此,如果我们需要保留缓存但允许请求修改 - 我们必须为每个请求提供其自己的对象副本。

那么,我们如何保护ASP.NET运行时缓存中的数据?

答案:通过以两种方式克隆对象(在插入缓存和从缓存读取时),使用深拷贝,并且让我强调“深层”。

让我向您展示我为这篇文章创建的一些代码示例。我创建了一个通用的CacheList 类,该类将允许用户向列表中添加通用类型并提供缓存和从缓存加载。我首先创建了一个辅助类,并添加了一个方法,该方法将允许我克隆一个列表。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Web;
using System.Web.Caching;
public static class GenericsHelper
{
    public static IList<T> CloneList<T>( IList<T> source ) where T : ICloneable
    {
        IList<T> clone = new List<T>( source.Count );
        foreach ( T t in source )
        {
            clone.Add( ( T )t.Clone( ) );
        }
        return clone;
    }
}

然后我实现了一个简单的通用列表,它将允许我

  • // call to add an item to our list 
    Add(T item) : void 
  • // call to insert list into the runtime cache 
    CacheIt(string cacheKey, DateTime absoluteExpiration) : void 
  • // call to load list from the data in the cache 
    // and allow mods without affecting cache 
    LoadCached(string cacheKey) : bool The generic type must implement ICloneable

我对列表进行了约束,指定我们只能存储实现ICloneable 接口的类型。让我指出,我假设此接口的实现正在执行深拷贝,而不是浅拷贝。

使用搜索引擎搜索ICloneable ,您无疑会找到各种关于它好坏的讨论,老实说,我可以看到反对使用此接口的论点。简而言之,开发人员建议这种方法不够清晰; 它没有指出实现它的对象是执行深拷贝还是浅拷贝。但是,如果您同意反对使用它的原因,那么您实际上没有任何问题;只是您自己编写的cloneable接口……也许您可以使其通用。

/// <summary>
/// A simple class to demonstrate how we can protect/preserve our data
/// in the cache.
/// </summary>
/// <typeparam name="T">Our generic type</typeparam>

public sealed class CacheList<T> where T : ICloneable
{
    private IList<T> _dataList = new List<T>( );

    public void Add( T item )
    {
        _dataList.Add( item );
    }

    public void CacheIt( string cacheKey, DateTime absoluteExpiration )
    {
        //
        // clone the data list 
        IList<T> clone = GenericsHelper.CloneList<T>( _dataList );

        //
        // insert into the runtime cache 
        HttpRuntime.Cache.Insert( cacheKey, clone, null,
        absoluteExpiration, Cache.NoSlidingExpiration );
    }

    public bool LoadCached( string cacheKey )
    {
        // 
        // perform thread-safe read from cache 
        object obj = HttpRuntime.Cache[ cacheKey ];

        // 
        // if this obj is not null then clone it, 
        // returning true to indicate clone success 
        if ( obj != null && obj is IList<T> )
        {
            _dataList = GenericsHelper.CloneList<T>( ( IList<T> )obj );

            return true;
        }
        return false;
    }

    public IList<T> DataList
    {
        get { return _dataList; }
    }
}

好吧,这就是使用克隆来保留我们缓存数据的代码。这是一个非常简单的例子,也是一个非常简化的列表实现。但是,我不想偏离这篇文章的目的。我将在未来扩展这篇文章,并向您展示这种缓存访问如何被封装在业务/域对象中。

我希望您喜欢这篇文章,还会有更多。我将在我的博客dotnet notepad上撰写更多关于C#、ASP.NET和设计的文章。

有用链接

历史

  • 2009年2月13日:初始帖子
© . All rights reserved.