首页 文章

如何在MVC应用程序中缓存数据

提问于
浏览
231

我在MVC应用程序中阅读了大量有关页面缓存和部分页面缓存的信息 . 但是,我想知道如何缓存数据 .

在我的场景中,我将使用LINQ to Entities(实体框架) . 在第一次调用GetNames(或任何方法)时,我想从数据库中获取数据 . 我想将结果保存在缓存中,并在第二次调用时使用缓存版本(如果存在) .

任何人都可以展示一个如何工作的例子,应该在哪里实现(模型?)以及它是否可行 .

我已经在传统的ASP.NET应用程序中看到了这一点,通常用于非常静态的数据 .

14 回答

  • 72

    在模型中引用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特定的,我一直使用这种方法来缓存数据 .

  • 371

    这是我使用的一个很好的简单缓存助手类/服务:

    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())
    
  • 24

    我指的是TT的帖子,并建议采用以下方法:

    Reference the System.Web dll in your model and use System.Web.Caching.Cache

    public string[] GetNames()
    { 
        var noms = Cache["names"];
        if(noms == null) 
        {    
            noms = DB.GetNames();
            Cache["names"] = noms; 
        }
    
        return ((string[])noms);
    }
    

    您不应该返回从缓存中重新读取的值,因为您永远不会知道在该特定时刻它是否仍在缓存中 . 即使你之前在声明中插入它,它可能已经消失或者从未被添加到缓存中 - 你只是不知道 .

    因此,您添加从数据库读取的数据并直接返回,而不是从缓存中重新读取 .

  • 27

    对于.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 .

    更多信息:

  • 0

    史蒂夫史密斯做了两篇很棒的博客文章,演示了如何在ASP.NET MVC中使用他的CachedRepository模式 . 它有效地使用存储库模式,允许您在不必更改现有代码的情况下获得缓存 .

    http://ardalis.com/Introducing-the-CachedRepository-Pattern

    http://ardalis.com/building-a-cachedrepository-via-strategy-pattern

    在这两篇文章中,他向您展示了如何设置此模式,并解释了它为何有用 . 通过使用此模式,您可以获得缓存,而无需现有代码查看任何缓存逻辑 . 实际上,您使用缓存的存储库就像它是任何其他存储库一样 .

  • 4

    AppFabric Caching 是一种分布式内存缓存技术,它使用跨多个服务器的物理内存将数据存储在键值对中 . AppFabric为.NET Framework应用程序提供了性能和可伸缩性改进 . Concepts and Architecture

  • -8

    扩展@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

    单项缓存(当每个项目根据其ID进行缓存时,因为缓存项目类型的整个目录将过于密集) .

    Product product = cache.Get("product_{0}", productId, 10, productData.getProductById);
    

    缓存所有的东西

    IEnumerable<Categories> categories = cache.Get("categories", 20, categoryData.getCategories);
    

    Why TId

    第二个帮手特别好,因为大多数数据键不是复合的 . 如果经常使用复合键,可以添加其他方法 . 通过这种方式,您可以避免执行各种字符串连接或string.Formats以获取传递给缓存帮助程序的密钥 . 它还使得传递数据访问方法变得更容易,因为您不必将ID传递给包装器方法......对于大多数用例来说,整个事情变得非常简洁和一致 .

  • 3

    这是对Hrvoje Hudo的回答的改进 . 此实现有几个关键改进:

    • 缓存键是根据更新数据的函数和传入的指定依赖关系的对象自动创建的

    • 传递任何缓存持续时间的时间 Span

    • 使用锁定线程安全

    请注意,这依赖于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)
    );
    
  • 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);
        }
    }
    
  • 3

    我用了两节课 . 第一个缓存核心对象:

    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; }
    }
    
  • 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
    
  • 41
    HttpContext.Current.Cache.Insert("subjectlist", subjectlist);
    
  • 3

    我以这种方式使用它,它对我有用 . 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;
    
    }
    
  • 1

    您还可以尝试使用ASP MVC中内置的缓存:

    将以下属性添加到您要缓存的控制器方法:

    [OutputCache(Duration=10)]
    

    在这种情况下,ActionResult将缓存10秒 .

    更多关于此here

相关问题