我在MVC应用程序中阅读了很多关于页面缓存和部分页面缓存的信息。但是,我想知道如何缓存数据。
在我的场景中,我将使用LINQ to Entities(实体框架)。在第一次调用GetNames(或任何方法)时,我想从数据库中获取数据。我希望将结果保存在缓存中,并在第二次调用时使用缓存版本(如果存在)。
任何人都可以展示一个如何工作的例子,应该在哪里实现(模型?)以及它是否有效。
我在传统的ASP.NET应用程序中看到过这种情况,通常用于非常静态的数据。
答案 0 :(得分:381)
这是我使用的一个很好的简单缓存助手类/服务:
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())
答案 1 :(得分:73)
在模型中引用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特有的,我一直使用这种方法来缓存数据。
答案 2 :(得分:42)
我指的是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);
}
您不应该返回从缓存中重新读取的值,因为您永远不会知道在特定时刻它是否仍在缓存中。即使你之前在声明中插入它,它可能已经消失或者从未被添加到缓存中 - 你只是不知道。
因此,您添加从数据库读取的数据并直接返回,而不是从缓存中重新读取。
答案 3 :(得分:28)
对于.NET 4.5+框架
添加参考:System.Runtime.Caching
添加使用声明:
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);
}
在.NET Framework 3.5及更早版本中,ASP.NET在System.Web.Caching命名空间中提供了内存缓存实现。在以前版本的.NET Framework中,缓存仅在System.Web命名空间中可用,因此需要依赖ASP.NET类。在.NET Framework 4中,System.Runtime.Caching命名空间包含为Web和非Web应用程序设计的API。
更多信息:
答案 4 :(得分:25)
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
在这两篇文章中,他向您展示了如何设置此模式,并解释了它有用的原因。通过使用此模式,您可以获得缓存,而无需现有代码查看任何缓存逻辑。基本上,您使用缓存的存储库就像它是任何其他存储库一样。
答案 5 :(得分:4)
AppFabric缓存以及内存缓存技术,该技术使用跨多个服务器的物理内存将数据存储在键值对中。 AppFabric为.NET Framework应用程序提供了性能和可伸缩性改进。 Concepts and Architecture
答案 6 :(得分:3)
扩展@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
第二个帮助器特别好,因为大多数数据键不是复合的。如果经常使用复合键,可以添加其他方法。这样就可以避免执行各种字符串连接或string.Formats来获取传递给缓存助手的密钥。它还使得传递数据访问方法变得更容易,因为您不必将ID传递给包装器方法......对于大多数用例来说,整个事情变得非常简洁和一致。
答案 7 :(得分:3)
这是对Hrvoje Hudo的回答的改进。这个实现有几个关键的改进:
请注意,这依赖于Newtonsoft.Json来序列化dependsOn对象,但是可以很容易地将其替换为任何其他序列化方法。
ICache.cs
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)
);
答案 8 :(得分:3)
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);
}
}
答案 9 :(得分:1)
我使用两个班级。第一个是缓存核心对象:
public class Cacher<TValue>
where TValue : class
{
#region Properties
private Func<TValue> _init;
public string Key { get; private set; }
public TValue Value
{
get
{
var item = HttpRuntime.Cache.Get(Key) as TValue;
if (item == null)
{
item = _init();
HttpContext.Current.Cache.Insert(Key, item);
}
return item;
}
}
#endregion
#region Constructor
public Cacher(string key, Func<TValue> init)
{
Key = key;
_init = init;
}
#endregion
#region Methods
public void Refresh()
{
HttpRuntime.Cache.Remove(Key);
}
#endregion
}
第二个是缓存对象列表:
public static class Caches
{
static Caches()
{
Languages = new Cacher<IEnumerable<Language>>("Languages", () =>
{
using (var context = new WordsContext())
{
return context.Languages.ToList();
}
});
}
public static Cacher<IEnumerable<Language>> Languages { get; private set; }
}
答案 10 :(得分:0)
我会说在这个持续存在的数据问题上实施Singleton可以解决这个问题,以防您发现以前的解决方案非常复杂
public class GPDataDictionary
{
private Dictionary<string, object> configDictionary = new Dictionary<string, object>();
/// <summary>
/// Configuration values dictionary
/// </summary>
public Dictionary<string, object> ConfigDictionary
{
get { return configDictionary; }
}
private static GPDataDictionary instance;
public static GPDataDictionary Instance
{
get
{
if (instance == null)
{
instance = new GPDataDictionary();
}
return instance;
}
}
// private constructor
private GPDataDictionary() { }
} // singleton
答案 11 :(得分:0)
HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
答案 12 :(得分:0)
我以这种方式使用它,它对我有用。 https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspx system.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;
}
答案 13 :(得分:0)
Oli下面的解决方案是商定的方法之一。我在想,因为在检查null时,noms可以为缓存引用非null,所以如果在函数返回之前,缓存过期或以某种方式变得无效(可能为null),noms不能引用另一个值吗?我觉得我可能会缺少一些东西。
public string[] GetNames()
{
string[] noms = Cache["names"];
if(noms == null)
{
noms = DB.GetNames();
Cache["names"] = noms;
}
return (noms);
}
谢谢!
答案 14 :(得分:-8)
您还可以尝试使用ASP MVC中内置的缓存:
将以下属性添加到您要缓存的控制器方法中:
[OutputCache(Duration=10)]
在这种情况下,ActionResult将缓存10秒钟。
有关此here
的更多信息