5

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

是否有可能做到这一点?

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

有人知道吗?

4

9 回答 9

4

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

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

这将允许您在不触及原始(缓存)列表的情况下过滤掉您想要的任何实例。

编辑:

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

如何在 c# 中使用 linq/lambda 获取数据副本而不是引用?

这应该让你走上正确的轨道。如果您还有其他问题,请告诉我。

于 2012-04-16T16:48:34.297 回答
3

缓存完整列表,然后使用 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 扩展方法将其恢复为列表中最初的对象类型。

于 2012-04-18T17:29:41.370 回答
3

Malcolm Frexner 所说的是一种很好的干净方式来解决您的问题,但可以使用 AutoMapper 大大简化。

假设您有一个要从中提取 DTO 对象的适配器(或者如果您不使用 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 服务!

于 2012-06-07T01:17:11.187 回答
2

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

领域模型

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 存储在缓存中,而无需为所有用户存储“可见”属性。

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

于 2012-06-06T22:02:42.720 回答
2

这应该有效:

IEnumerable<T> things = GetThings();

Cache["ThingCache"] = things;

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

//Cache["ThingCache"] should still contain the original collection.
于 2012-04-16T09:09:18.260 回答
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。克隆,这是我在所有复杂对象中创建的自定义方法。

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

于 2012-06-05T13:24:52.620 回答
1

最简单的方法是在将对象放入缓存之前将它们序列化为 json,然后在将它们从缓存中取出时反序列化。这样你就不必对你的类做任何改变,比如实现 iclonable 或类似的。

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

接着

var cachedObject = (string)Cache[ComposeKey(key, domain)];
return JsonConvert.DeserializeObject<T>(cachedObject);
于 2014-06-27T11:49:17.830 回答
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;


    }
}
于 2014-10-27T15:03:33.913 回答
0

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

您正在寻找 ImmutableList。

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

于 2015-09-18T23:15:04.310 回答