asp.net-mvc 如何在 MVC 应用程序中缓存数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/343899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to cache data in a MVC application
提问by Coolcoder
I have read lots of information about page caching and partial page caching in a MVC application. However, I would like to know how you would cache data.
我已经阅读了很多关于 MVC 应用程序中的页面缓存和部分页面缓存的信息。但是,我想知道您将如何缓存数据。
In my scenario I will be using LINQ to Entities (entity framework). On the first call to GetNames (or whatever the method is) I want to grab the data from the database. I want to save the results in cache and on the second call to use the cached version if it exists.
在我的场景中,我将使用 LINQ to Entities(实体框架)。在第一次调用 GetNames(或任何方法)时,我想从数据库中获取数据。我想将结果保存在缓存中,并在第二次调用时使用缓存版本(如果存在)。
Can anyone show an example of how this would work, where this should be implemented (model?) and if it would work.
任何人都可以举例说明这将如何工作,应该在哪里实现(模型?)以及它是否可以工作。
I have seen this done in traditional ASP.NET apps , typically for very static data.
我已经在传统的 ASP.NET 应用程序中看到了这一点,通常用于非常静态的数据。
采纳答案by terjetyl
Reference the System.Web dll in your model and use System.Web.Caching.Cache
在您的模型中引用 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;
}
A bit simplified but I guess that would work. This is not MVC specific and I have always used this method for caching data.
有点简化,但我想这会奏效。这不是特定于 MVC 的,我一直使用这种方法来缓存数据。
回答by Hrvoje Hudo
Here's a nice and simple cache helper class/service I use:
这是我使用的一个很好且简单的缓存助手类/服务:
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;
}
Usage:
用法:
cacheProvider.GetOrSet("cache key", (delegate method if cache is empty));
Cache provider will check if there's anything by the name of "cache id" in the cache, and if there's not, it will call a delegate method to fetch data and store it in cache.
缓存提供者将检查缓存中是否有名为“缓存 id”的内容,如果没有,它将调用委托方法来获取数据并将其存储在缓存中。
Example:
例子:
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
回答by Oli
I'm referring to TT's post and suggest the following approach:
我指的是 TT 的帖子并建议采用以下方法:
Reference the System.Web dll in your model and use System.Web.Caching.Cache
在您的模型中引用 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);
}
You should not return a value re-read from the cache, since you'll never know if at that specific moment it is still in the cache. Even if you inserted it in the statement before, it might already be gone or has never been added to the cache - you just don't know.
您不应返回从缓存中重新读取的值,因为您永远不会知道在那个特定时刻它是否仍在缓存中。即使您之前将它插入到语句中,它也可能已经消失或从未添加到缓存中——您只是不知道。
So you add the data read from the database and return it directly, not re-reading from the cache.
所以你把从数据库中读取的数据加入直接返回,而不是从缓存中重新读取。
回答by juFo
For .NET 4.5+ framework
对于 .NET 4.5+ 框架
add reference: System.Runtime.Caching
添加参考: System.Runtime.Caching
add using statement:
using 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);
}
In the .NET Framework 3.5 and earlier versions, ASP.NET provided an in-memory cache implementation in the System.Web.Caching namespace. In previous versions of the .NET Framework, caching was available only in the System.Web namespace and therefore required a dependency on ASP.NET classes. In the .NET Framework 4, the System.Runtime.Caching namespace contains APIs that are designed for both Web and non-Web applications.
在 .NET Framework 3.5 和更早版本中,ASP.NET 在 System.Web.Caching 命名空间中提供了内存缓存实现。在 .NET Framework 的早期版本中,缓存仅在 System.Web 命名空间中可用,因此需要依赖于 ASP.NET 类。在 .NET Framework 4 中,System.Runtime.Caching 命名空间包含专为 Web 和非 Web 应用程序设计的 API。
More info:
更多信息:
回答by Brendan Enrick
Steve Smith did two great blog posts which demonstrate how to use his CachedRepository pattern in ASP.NET MVC. It uses the repository pattern effectively and allows you to get caching without having to change your existing code.
Steve Smith 发表了两篇很棒的博客文章,展示了如何在 ASP.NET MVC 中使用他的 CachedRepository 模式。它有效地使用存储库模式并允许您在无需更改现有代码的情况下获得缓存。
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
In these two posts he shows you how to set up this pattern and also explains why it is useful. By using this pattern you get caching without your existing code seeing any of the caching logic. Essentially you use the cached repository as if it were any other repository.
在这两篇文章中,他向您展示了如何设置此模式,并解释了它为何有用。通过使用这种模式,您可以获得缓存,而您现有的代码不会看到任何缓存逻辑。本质上,您可以像使用任何其他存储库一样使用缓存存储库。
回答by Arun Duth
AppFabric Cachingis distributed and an in-memory caching technic that stores data in key-value pairs using physical memory across multiple servers. AppFabric provides performance and scalability improvements for .NET Framework applications. Concepts and Architecture
AppFabric 缓存是一种分布式内存缓存技术,它使用跨多个服务器的物理内存将数据存储在键值对中。AppFabric 为 .NET Framework 应用程序提供性能和可伸缩性改进。概念和架构
回答by Chau
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);
}
}
回答by smdrager
Extending @Hrvoje Hudo's answer...
扩展@Hrvoje Hudo 的回答...
Code:
代码:
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;
}
Examples
例子
Single item caching (when each item is cached based on its ID because caching the entire catalog for the item type would be too intensive).
单个项目缓存(当每个项目根据其 ID 进行缓存时,因为缓存项目类型的整个目录会过于密集)。
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
Caching all of something
缓存所有东西
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
Why TId
为什么是 TId
The second helper is especially nice because most data keys are not composite. Additional methods could be added if you use composite keys often. In this way you avoid doing all sorts of string concatenation or string.Formats to get the key to pass to the cache helper. It also makes passing the data access method easier because you don't have to pass the ID into the wrapper method... the whole thing becomes very terse and consistant for the majority of use cases.
第二个助手特别好,因为大多数数据键不是复合的。如果您经常使用复合键,则可以添加其他方法。通过这种方式,您可以避免进行各种字符串连接或 string.Formats 来获取传递给缓存助手的键。它还使传递数据访问方法变得更容易,因为您不必将 ID 传递到包装器方法中……对于大多数用例,整个事情变得非常简洁和一致。
回答by DShook
Here's an improvement to Hrvoje Hudo's answer. This implementation has a couple of key improvements:
这是对 Hrvoje Hudo 答案的改进。这个实现有几个关键的改进:
- Cache keys are created automatically based on the function to update data and the object passed in that specifies dependencies
- Pass in time span for any cache duration
- Uses a lock for thread safety
- 缓存键是根据更新数据的函数和传入的指定依赖项的对象自动创建的
- 传入任何缓存持续时间的时间跨度
- 使用锁来保证线程安全
Note that this has a dependency on Newtonsoft.Json to serialize the dependsOn object, but that can be easily swapped out for any other serialization method.
请注意,这依赖于 Newtonsoft.Json 来序列化 dependsOn 对象,但可以轻松地将其替换为任何其他序列化方法。
ICache.cs
缓存文件
public interface ICache
{
T GetOrSet<T>(Func<T> getItemCallback, object dependsOn, TimeSpan duration) where T : class;
}
InMemoryCache.cs
内存缓存.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;
}
}
Usage:
用法:
var order = _cache.GetOrSet(
() => _session.Set<Order>().SingleOrDefault(o => o.Id == orderId)
, new { id = orderId }
, new TimeSpan(0, 10, 0)
);
回答by user3776645
I have used it in this way and it works for me. https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspxparameters info for system.web.caching.cache.add.
我以这种方式使用它,它对我有用。 https://msdn.microsoft.com/en-us/library/system.web.caching.cache.add(v=vs.110).aspxsystem.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;
}

