我阅读了很多有关MVC应用程序中页面缓存和部分页面缓存的信息。然而,我想知道你如何缓存数据。
在我的情况下,我将使用LINQ to Entities(实体框架)。在第一次调用GetNames(或者其他方法)时,我想从数据库中获取数据。我希望将结果保存在缓存中,并在第二次调用时如果存在缓存版本则使用它。
是否可以给出一个示例说明如何实现,应该在哪里实现(模型?),以及是否有效。
我看到过这种做法在传统的ASP.NET应用程序中完成,通常用于非常静态的数据。
我阅读了很多有关MVC应用程序中页面缓存和部分页面缓存的信息。然而,我想知道你如何缓存数据。
在我的情况下,我将使用LINQ to Entities(实体框架)。在第一次调用GetNames(或者其他方法)时,我想从数据库中获取数据。我希望将结果保存在缓存中,并在第二次调用时如果存在缓存版本则使用它。
是否可以给出一个示例说明如何实现,应该在哪里实现(模型?),以及是否有效。
我看到过这种做法在传统的ASP.NET应用程序中完成,通常用于非常静态的数据。
这是一个我使用的简单而实用的缓存帮助类/服务:
using System.Runtime.Caching;
public class InMemoryCache: ICacheService
{
public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
{
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(10));
}
return item;
}
}
interface ICacheService
{
T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}
cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));
缓存提供者会检查缓存中是否存在名为“cache id”的内容,如果不存在,则会调用委托方法来获取数据并将其存储在缓存中。
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
在您的模型中引用 System.Web
dll,并使用 System.Web.Caching.Cache
public string[] GetNames()
{
string[] names = Cache["names"] as string[];
if(names == null) //not in cache
{
names = DB.GetNames();
Cache["names"] = names;
}
return names;
}
有点简化,但我认为会有效。这不是MVC特定的,我一直使用这种方法缓存数据。
DB.GetNames().AsQueryable
方法? - Chase Florellif ((names = Cache["names"]) == null) { . . .
? - BenR. . . Cache["names"] = names = DB.GetNames(); . . .
- stenlytw我参考了TT的帖子并建议采用以下方法:
在您的模型中引用System.Web DLL并使用System.Web.Caching.Cache
public string[] GetNames()
{
var noms = Cache["names"];
if(noms == null)
{
noms = DB.GetNames();
Cache["names"] = noms;
}
return ((string[])noms);
}
不应该返回从缓存中重新读取的值,因为您永远不会知道在那个特定时刻它是否仍然在缓存中。即使您在之前的语句中插入了它,它可能已经丢失或从未添加到缓存中,您只是不知道。
因此,您应该添加从数据库中读取的数据并直接返回它,而不是从缓存中重新读取。
Cache["names"] = noms;
这行代码不是将数据放入缓存中吗? - Omar对于.NET 4.5+框架
添加引用:System.Runtime.Caching
添加using语句:using System.Runtime.Caching;
public string[] GetNames()
{
var noms = System.Runtime.Caching.MemoryCache.Default["names"];
if(noms == null)
{
noms = DB.GetNames();
System.Runtime.Caching.MemoryCache.Default["names"] = noms;
}
return ((string[])noms);
}
Db.GetNames()
是从哪里来的? - JuniorSteve Smith撰写了两篇博客文章,演示如何在ASP.NET MVC中使用他的CachedRepository模式。这个模式有效地使用了存储库模式,允许您获得缓存而不必更改现有的代码。
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
在这两篇文章中,他向您展示如何设置这种模式,并解释了它的有用性。使用这个模式,您可以获得缓存,但您的现有代码不会看到任何缓存逻辑。基本上,您可以像使用任何其他存储库一样使用缓存存储库。
我已经以这种方式使用它并且对我起作用。 https://msdn.microsoft.com/zh-cn/library/system.web.caching.cache.add(v=vs.110).aspx 系统.web.caching.cache.add的参数信息。
public string GetInfo()
{
string name = string.Empty;
if(System.Web.HttpContext.Current.Cache["KeyName"] == null)
{
name = GetNameMethod();
System.Web.HttpContext.Current.Cache.Add("KeyName", name, null, DateTime.Noew.AddMinutes(5), Cache.NoSlidingExpiration, CacheitemPriority.AboveNormal, null);
}
else
{
name = System.Web.HttpContext.Current.Cache["KeyName"] as string;
}
return name;
}
AppFabric Caching 是一种分布式的、基于内存的缓存技术,它使用多台服务器上的物理内存存储数据的键值对。AppFabric 为 .NET Framework 应用程序提供了性能和可伸缩性的提升。 概念和架构
延伸 @Hrvoje Hudo 的答案...
代码:
using System;
using System.Runtime.Caching;
public class InMemoryCache : ICacheService
{
public TValue Get<TValue>(string cacheKey, int durationInMinutes, Func<TValue> getItemCallback) where TValue : class
{
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
public TValue Get<TValue, TId>(string cacheKeyFormat, TId id, int durationInMinutes, Func<TId, TValue> getItemCallback) where TValue : class
{
string cacheKey = string.Format(cacheKeyFormat, id);
TValue item = MemoryCache.Default.Get(cacheKey) as TValue;
if (item == null)
{
item = getItemCallback(id);
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(durationInMinutes));
}
return item;
}
}
interface ICacheService
{
TValue Get<TValue>(string cacheKey, Func<TValue> getItemCallback) where TValue : class;
TValue Get<TValue, TId>(string cacheKeyFormat, TId id, Func<TId, TValue> getItemCallback) where TValue : class;
}
示例
单个项目缓存(当每个项目基于其ID进行缓存,因为对于该项目类型缓存整个目录将过于密集)。
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
TId的原因
第二个辅助器尤其好用,因为大多数数据键都不是复合键。如果您经常使用复合键,可以添加其他方法。这样可以避免做各种字符串连接或字符串格式化来获取要传递给缓存辅助程序的键。它还使得传递数据访问方法更加容易,因为您不需要将ID传递到包装器方法中...整个过程对于大多数用例来说非常简洁和一致。
public sealed class CacheManager
{
private static volatile CacheManager instance;
private static object syncRoot = new Object();
private ObjectCache cache = null;
private CacheItemPolicy defaultCacheItemPolicy = null;
private CacheEntryRemovedCallback callback = null;
private bool allowCache = true;
private CacheManager()
{
cache = MemoryCache.Default;
callback = new CacheEntryRemovedCallback(this.CachedItemRemovedCallback);
defaultCacheItemPolicy = new CacheItemPolicy();
defaultCacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1.0);
defaultCacheItemPolicy.RemovedCallback = callback;
allowCache = StringUtils.Str2Bool(ConfigurationManager.AppSettings["AllowCache"]); ;
}
public static CacheManager Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new CacheManager();
}
}
}
return instance;
}
}
public IEnumerable GetCache(String Key)
{
if (Key == null || !allowCache)
{
return null;
}
try
{
String Key_ = Key;
if (cache.Contains(Key_))
{
return (IEnumerable)cache.Get(Key_);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}
public void ClearCache(string key)
{
AddCache(key, null);
}
public bool AddCache(String Key, IEnumerable data, CacheItemPolicy cacheItemPolicy = null)
{
if (!allowCache) return true;
try
{
if (Key == null)
{
return false;
}
if (cacheItemPolicy == null)
{
cacheItemPolicy = defaultCacheItemPolicy;
}
String Key_ = Key;
lock (Key_)
{
return cache.Add(Key_, data, cacheItemPolicy);
}
}
catch (Exception)
{
return false;
}
}
private void CachedItemRemovedCallback(CacheEntryRemovedArguments arguments)
{
String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
LogManager.Instance.Info(strLog);
}
}
public interface ICache
{
T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}
InMemoryCache.cs
using System;
using System.Reflection;
using System.Runtime.Caching;
using Newtonsoft.Json;
public class InMemoryCache : ICache
{
private static readonly object CacheLockObject = new object();
public T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class
{
string cacheKey = GetCacheKey(getItemCallback, dependsOn);
T item = MemoryCache.Default.Get(cacheKey) as T;
if (item == null)
{
lock (CacheLockObject)
{
item = getItemCallback();
MemoryCache.Default.Add(cacheKey, item, DateTime.Now.Add(duration));
}
}
return item;
}
private string GetCacheKey<T>(Func<T> itemCallback, object dependsOn) where T: class
{
var serializedDependants = JsonConvert.SerializeObject(dependsOn);
var methodType = itemCallback.GetType();
return methodType.FullName + serializedDependants;
}
}
使用方法:
var order = _cache.GetOrSet(
() => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
, new { id = orderId }
, new TimeSpan(0, 10, 0)
);
if (item == null)
应该在锁内。现在,当此if
在锁之前时,可能会发生竞争条件。或者更好的做法是,在锁之前保留if
,但是在锁内的第一行重新检查缓存是否仍为空。因为如果两个线程同时到达,它们都会更新缓存。您当前的锁没有帮助。 - Al Kepp