我想在 asp.net mvc 中缓存一个数据集合并过滤这个集合而不更改缓存的集合。
是否有可能做到这一点?
因为现在,当我获得缓存集合时,我会得到对它的引用,并且该集合的过滤数据会更改缓存集合。
有人知道吗?
我想在 asp.net mvc 中缓存一个数据集合并过滤这个集合而不更改缓存的集合。
是否有可能做到这一点?
因为现在,当我获得缓存集合时,我会得到对它的引用,并且该集合的过滤数据会更改缓存集合。
有人知道吗?
只需将缓存列表中的项目复制到新列表即可。实例将相同,但不会触及原始(缓存)列表。像这样的东西:
List<[CLASS]> collectionCached; //assuming this this your cached list
List<[CLASS]> collectionWorking = new List<[CLASS]>();
collectionWorking.AddRange(collectionCached);
这将允许您在不触及原始(缓存)列表的情况下过滤掉您想要的任何实例。
编辑:
在 OP 进一步澄清后,似乎有必要复制实体本身。复制参考对象背后的基本思想称为“克隆”。这会制作原始实体的副本,并允许您更改副本而不更改原始实例。SO 有一些很好的链接,这里有一个讨论克隆和 LINQ 的概念。
如何在 c# 中使用 linq/lambda 获取数据副本而不是引用?
这应该让你走上正确的轨道。如果您还有其他问题,请告诉我。
缓存完整列表,然后使用 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 扩展方法将其恢复为列表中最初的对象类型。
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 服务!
当我直接在视图中使用我的域模型时,我遇到了这个问题。只要将所有显示的数据放入专用的 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 存储在缓存中,而无需为所有用户存储“可见”属性。
如果你有对象列表,它会变得有点困难,但这种模式适用于那里。
这应该有效:
IEnumerable<T> things = GetThings();
Cache["ThingCache"] = things;
T aThing = ((IEnumerable)Cache["ThingCache"]).First();
//Cache["ThingCache"] should still contain the original collection.
我终于解决了我的问题,方法是在我的每个对象中创建一个 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。克隆,这是我在所有复杂对象中创建的自定义方法。
现在它工作正常......
最简单的方法是在将对象放入缓存之前将它们序列化为 json,然后在将它们从缓存中取出时反序列化。这样你就不必对你的类做任何改变,比如实现 iclonable 或类似的。
Cache.Set(key, JsonConvert.SerializeObject(objectToCache), policy);
接着
var cachedObject = (string)Cache[ComposeKey(key, domain)];
return JsonConvert.DeserializeObject<T>(cachedObject);
这是我处理缓存中毒时的首选代码:
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;
}
}
这可以使用 System.Collections.Immutable 解决。
您正在寻找 ImmutableList。
在此处获取 nuget-package:https ://www.nuget.org/packages/System.Collections.Immutable/ 或:Install-Package System.Collections.Immutable