如何在MVC应用程序中caching数据
我已经阅读了很多有关页面caching和部分页面caching在MVC应用程序中的信息。 不过,我想知道你将如何caching数据。
在我的场景中,我将使用LINQ to Entities(entity framework)。 在第一次调用GetNames(或任何方法)时,我想从数据库中获取数据。 我想将结果保存在caching中,并在第二次调用时使用caching版本(如果存在的话)。
任何人都可以显示一个例子,这将如何工作,这应该实施(模型?),如果它能工作。
我已经看到这在传统的ASP.NET应用程序中完成,通常用于非常静态的数据。
引用您的模型中的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,我一直使用这种方法来caching数据。
这是一个很好的,简单的caching助手类/服务我使用:
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));
caching提供程序将检查caching中是否有“cachingID”的名称,如果不存在,它将调用委托方法来获取数据并将其存储在caching中。
例:
var products=cacheService.GetOrSet("catalog.products", ()=>productRepository.GetAll())
我指的是TT的post,并提出以下方法:
引用您的模型中的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); }
您不应该返回从caching中重新读取的值,因为您永远不会知道在该特定时刻是否仍然在caching中。 即使您之前将其插入到语句中,它可能已经消失或从未添加到caching – 您只是不知道。
因此,您添加从数据库中读取的数据并直接返回,而不是从caching中重新读取。
史蒂夫·史密斯做了两篇很棒的博文,演示了如何在ASP.NET MVC中使用他的CachedRepository模式。 它有效地使用存储库模式,并允许您获取caching而无需更改现有的代码。
http://ardalis.com/Introducing-the-CachedRepository-Pattern
http://ardalis.com/building-a-cachedrepository-via-strategy-pattern
在这两篇文章中,他向你展示了如何设置这个模式,并解释了为什么它是有用的。 通过使用这种模式,您可以在不存在您现有的代码的情况下caching任何caching逻辑。 本质上,您使用caching的存储库就好像它是任何其他存储库。
对于.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命名空间中提供了一个内存中caching实现。 在以前版本的.NET Framework中,caching仅在System.Web命名空间中可用,因此需要依赖于ASP.NET类。 在.NET Framework 4中,System.Runtime.Caching命名空间包含为Web和非Web应用程序devise的API。
更多信息: https : //msdn.microsoft.com/en-us/library/dd997357(v=vs.110).aspx
AppFabriccaching是分布式的内存caching技术,它使用多个服务器上的物理内存将数据存储在键值对中。 AppFabric为.NET Framework应用程序提供了性能和可伸缩性改进。 概念和build筑
扩展@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; }
例子
单个项目caching(当每个项目被caching基于它的ID,因为caching项目types的整个目录将是太密集)。
Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
caching所有的东西
IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
为什么TId
第二个帮手特别好,因为大多数数据键不是复合的。 如果您经常使用复合键,则可以添加其他方法。 通过这种方式,您可以避免使用各种string连接或string.Formats来将密钥传递给caching助手。 它也使传递数据访问方法变得更容易,因为您不必将ID传递给包装器方法…整个事情对于大多数用例来说变得非常简洁和一致。
这是Hrvoje Hudo的回答的一个改进。 这个实现有一些关键的改进:
- caching键是根据更新数据的函数自动创build的,传入的对象指定了依赖关系
- 通过任何caching持续时间的时间跨度
- 使用locking线程安全
请注意,这依赖于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) );
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); } }
我使用两个类。 第一个caching核心对象:
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 }
第二个是caching对象列表:
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; } }
我会说在这个持续的数据问题上实现Singleton可以解决这个问题,以防万一你发现以前的解决scheme非常复杂
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
您也可以尝试使用ASP MVC内置的caching:
将以下属性添加到您要caching的控制器方法:
[OutputCache(Duration=10)]
在这种情况下,这个ActionResult将被caching10秒钟。
更多关于这里