缓存集合并对其进行过滤,而无需在ASP.NET中修改原始缓存集合

时间:2012-04-16 08:57:09

标签: c# asp.net asp.net-mvc caching collections

我想在asp.net mvc中缓存数据集并过滤此集合而不更改缓存集合。

是否可以这样做?

因为现在,当我获得我的缓存集合时,我会得到它的引用,并且过滤此集合的数据会更改缓存的集合。

有人知道吗?

9 个答案:

答案 0 :(得分:4)

只需将缓存列表中的项目复制到新列表即可。实例将是相同的,但不会触及原始(缓存)列表。像这样:

    List<[CLASS]> collectionCached; //assuming this this your cached list
    List<[CLASS]> collectionWorking = new List<[CLASS]>();
    collectionWorking.AddRange(collectionCached);

这将允许您过滤掉所需的任何实例,而无需触及原始(缓存)列表。

编辑:

在OP进一步澄清之后,似乎需要制作实体本身的副本。制作参考对象副本的基本思想称为“克隆”。这将生成原始实体的副本,并允许您在不更改原始实例的情况下更改副本。 SO有一些很好的链接,这里讨论克隆和LINQ的概念。

How to get a copy of data instead of a reference using linq/lambda in c#?

这可以让你走上正轨。如果您有任何其他问题,请与我们联系。

答案 1 :(得分:3)

Malcolm Frexner所说的是一种解决问题的简洁方法,但使用AutoMapper可以大大简化。

假设您有一个正在从中提取DTO对象的适配器(或者如果您直接从数据库中提取WCF,则使用WCF,同样的概念适用)

public class AccountServiceAdapter
{
     private List<Accounts> _accounts
     {
          get
          {
               if(HttpContext.Current.Cache["accounts"] == null)
                    HttpContext.Current.Cache["accounts"] = default(List<Accounts>());
               return (List<Accounts>)HttpContext.Current.Cache["accounts"];
          }
          set
          {
               HttpContext.Current.Cache["accounts"] = value;
          }
     }

     public List<AccountViewModel> FetchAccounts()
     {
          if(_accounts == default(List<Accounts>))
          {
               //Do Fetch From Service or Persistence and set _accounts
          }

          return _accounts.Select(x => x.ConvertToViewModel()).ToList();
     }
}

那就是你可能已经写好的缓存片了。接下来是您可以享受一些乐趣的地方。我不会解释AutoMapper的所有映射乐趣,因为有十亿个链接可以做到,但我会给你一些片段来帮助你。

https://github.com/AutoMapper/AutoMapper/wiki/Projection

使用NuGet添加对AutoMapper的引用后,您需要创建一个ProjectionManager类。

public static class ProjectionManager
{
     ProjectionManager()
     {
          //The mapping exercise is AS SIMPLE as this if all you are doing is
          //using the exact same property names, it'll autowire
          //If you use different projections you can create complex maps see the link
          //above.
          Mapper.CreateMap<Account,AccountViewModel>();
          Mapper.CreateMap<AccountViewModel,Account>();
     }

     //Make yourself some handy extension methods for syntactic sugar
     public static AccountViewModel ConvertToViewModel(this Account x)
     {
          return Mapper.Map<Account,AccountViewModel>(x);
     }

     //Make yourself some handy extension methods for syntactic sugar
     public static Account ConvertFromViewModel(this AccountViewModel x)
     {
          return Mapper.Map<AccountViewModel,Account>(x);
     }
}

完成所有这些操作后,您的缓存现在变得非常简单,启动后您可以将数据转换为具有验证,显示名称等所有简洁功能的视图模型,而不必弄乱您的实体对象或您的DTO对象退出WCF服务!

答案 2 :(得分:2)

这应该有效:

IEnumerable<T> things = GetThings();

Cache["ThingCache"] = things;

T aThing = ((IEnumerable)Cache["ThingCache"]).First();

//Cache["ThingCache"] should still contain the original collection.

答案 3 :(得分:2)

缓存完整列表,然后使用Linq过滤集合。

var list = GetFromCache();
var filtered = list.Where(x => x.Name.Contains("rob"));

编辑:

获得此筛选列表时,是否要更改列表中包含的对象?是的,这也将反映在缓存列表中的同一个实例中。但在这一点上,听起来这不是很好的缓存数据。通常,缓存的数据不会更改,或者您可以在一段时间内看不到更改。如果数据准确性更重要,请不要缓存。

如果你要更改列表中的项目状态,你或许可以得到你想要的东西:

var filtered = list.Where(x => x.Name.Contains("rob")).Select(x => x.MemberwiseClone()).Cast<YourObjType>();

这使用MemberwiseClone执行浅拷贝;如果这还不够好,你需要对一个保存副本的方法做一些工作。它返回Object,因此我使用Cast扩展方法将其恢复为最初在列表中的对象类型。

答案 4 :(得分:2)

我终于通过在每个对象中创建一个Clone()方法来解决我的问题,并在从缓存中检索它之后立即调用我的主对象上的Clone()方法。

我的主要对象是类型帐户,其中包含自己的域名列表,其中包含产品列表等...

以下是Account对象中的Clone方法:

    public Account Clone()
    {
        // Copy all properties of the object in a new object account
        var account = (Account)this.MemberwiseClone();

        var domainsList = new List<Domain>();
        foreach (var d in this.Domains)
        {
            domainsList.Add(d.Clone());
        }

        account.Domains = domainsList;

        return account;
    }

就像你可以看到我使用MemberwiseClone()来复制简单属性,然后我再次在所有复杂对象上调用Clone。克隆,这是我在所有复杂对象中创建的自定义方法。

现在它工作正常......

答案 5 :(得分:2)

当我在View中直接使用我的域模型时遇到了这个问题。只要将所有显示的数据放在专用的ViewModel中,就可以轻松解决问题。

<强>的DomainModel

public class PersonInfo
{
     public int Age {get;set;}
     public string Name {get;set;}
}

<强>视图模型

public class PersonViewModel
{
     public PersonInfo PersonData {get;set;}
     public bool Visible {get;set;}
}

现在,您可以将PersonInfo存储在缓存中,而不会为所有用户存储“Visible”属性。

如果你有对象列表会有点困难,但这种模式适用于那里。

答案 6 :(得分:1)

最简单的方法是将对象序列化为json,然后再将它们放入缓存中,然后在将它们移出缓存时进行反序列化。 这样您就不必对类进行任何更改,例如实现iclonable或类似的。

Cache.Set(key, JsonConvert.SerializeObject(objectToCache), policy);

然后

var cachedObject = (string)Cache[ComposeKey(key, domain)];
return JsonConvert.DeserializeObject<T>(cachedObject);

答案 7 :(得分:0)

这是处理缓存中毒时的首选代码:

    var list_from_cache = HttpContext.Current.Cache["females"] as List<Females>;                
    var females = new List<Females>();
    females.AddRange(list_from_cache.Select(n => Cloner(n))); //clone all objects
   //now we can change the objects as much as we want without changing the cache
    females[0].Height=160;

克隆人的代码,把它放在某个地方:(记得把[Serializable]放在你的类上克隆)

public static T Cloner<T>(T source) where T : class
{
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", "source");
    }

    // Don't serialize a null object, simply return the default for that object
    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        //return    (T)formatter.Deserialize(stream);
        var clone = formatter.Deserialize(stream) as T;
        return clone;


    }
}

答案 8 :(得分:0)

这可以使用System.Collections.Immutable解决。

您正在寻找ImmutableList。

在此处获取nuget-package:https://www.nuget.org/packages/System.Collections.Immutable/ 或:Install-Package System.Collections.Immutable