【问题标题】:Caching in a console application在控制台应用程序中缓存
【发布时间】:2011-11-27 19:44:01
【问题描述】:

我需要缓存一个通用列表,这样我就不必多次查询数据库。在 Web 应用程序中,我只需将其添加到 httpcontext.current.cache 即可。在控制台应用程序中缓存对象的正确方法是什么?

【问题讨论】:

  • @Josh:这绝对是 not 该问题的重复,因为问题是“在控制台中缓存对象的 正确 方法是什么应用”。 System.Web.Caching.Cache 不是在控制台应用程序中缓存对象的正确方法。
  • @MusiGenesis:在 .Net 4.0 之前,这是一种非常有效的缓存方式。 hanselman.com/blog/UsingTheASPNETCacheOutsideOfASPNET.aspx
  • @Josh:我在这里遗漏了什么吗?在 ASP.NET 中,页面是无状态的,因此在页面生命周期之后将内容保存在内存中需要 System.Web 缓存之类的东西。控制台应用程序没有这样的问题。 Hanselman 的博文没有解释为什么使用 System.Web.Caching.Cache 是必要或可取的 - 它只是说明这是可能的(当然是这样)。
  • @MusiGenesis:缓存不仅仅是“将内容保存在内存中”。它提供线程安全,以及让项目过期的能力,包括绝对的和滑动的。当人们希望避免进行潜在的昂贵调用来检索数据时,在任何应用程序中使用它都是可取的。

标签: c# caching console


【解决方案1】:

将其作为包含类的实例成员。在网络应用程序中,您不能这样做,因为每次请求都会重新创建页面类的对象。

但是 .NET 4.0 也有 MemoryCache 用于此目的的类。

【讨论】:

    【解决方案2】:
    // Consider this psuedo code for using Cache
    public DataSet GetMySearchData(string search)
    {
        // if it is in my cache already (notice search criteria is the cache key)
        string cacheKey = "Search " + search;
        if (Cache[cacheKey] != null)
        {
            return (DataSet)(Cache[cacheKey]);
        }
        else
        {
            DataSet result = yourDAL.DoSearch(search);
            Cache[cacheKey].Insert(result);  // There are more params needed here...
            return result;
        }
    }
    

    参考:How do I cache a dataset to stop round trips to db?

    【讨论】:

      【解决方案3】:

      在类级变量中。据推测,在您的控制台应用程序的 main 方法中,您至少实例化了某种对象。在这个对象的类中,您声明一个类级变量(List<String> 或其他),您可以在其中缓存需要缓存的任何内容。

      【讨论】:

      • 有些人会投反对票,这样答案就会被投赞成票。我把它弄平了别担心。
      【解决方案4】:

      【讨论】:

      • 如果需要单例行为,最好使用 IoC 容器(或类似容器),而不是直接使用单例模式。单例模式很容易成为反模式,例如导致测试出现问题。
      • @chibac:这是一个 Console 应用程序。 IoC = YAGNI。
      • @Henk 可能,这在上下文中是不可能的。你显然没有遇到我拥有的控制台应用程序级别。你肯定能想到一个可能很复杂的控制台应用程序的例子吗?如果它是矫枉过正,那么那将是“或类似的”。控制台应用也应该经过测试。
      • @chibacity 使用不当的 IoC 容器可能比手工制作的 Singleton 更糟糕。我们都知道,但问题是关于“如何缓存”,而不是如何处理单例:)
      • @dario 我显然不建议糟糕地使用 IoC 容器,那将是愚蠢的。如果你直接使用 Singleton 模式,你将很难测试。我确实说过“或类似的”,我不是教条……
      【解决方案5】:

      有很多方法可以实现缓存,具体取决于您正在做什么。通常您将使用字典来保存缓存值。这是我对缓存的简单实现,它仅在有限的时间内缓存值:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      namespace CySoft.Collections
      {
          public class Cache<TKey,TValue>
          {
              private readonly Dictionary<TKey, CacheItem> _cache = new Dictionary<TKey, CacheItem>();
              private TimeSpan _maxCachingTime;
      
              /// <summary>
              /// Creates a cache which holds the cached values for an infinite time.
              /// </summary>
              public Cache()
                  : this(TimeSpan.MaxValue)
              {
              }
      
              /// <summary>
              /// Creates a cache which holds the cached values for a limited time only.
              /// </summary>
              /// <param name="maxCachingTime">Maximum time for which the a value is to be hold in the cache.</param>
              public Cache(TimeSpan maxCachingTime)
              {
                  _maxCachingTime = maxCachingTime;
              }
      
              /// <summary>
              /// Tries to get a value from the cache. If the cache contains the value and the maximum caching time is
              /// not exceeded (if any is defined), then the cached value is returned, else a new value is created.
              /// </summary>
              /// <param name="key">Key of the value to get.</param>
              /// <param name="createValue">Function creating a new value.</param>
              /// <returns>A cached or a new value.</returns>
              public TValue Get(TKey key, Func<TValue> createValue)
              {
                  CacheItem cacheItem;
                  if (_cache.TryGetValue(key, out cacheItem) && (DateTime.Now - cacheItem.CacheTime) <= _maxCachingTime) {
                      return cacheItem.Item;
                  }
                  TValue value = createValue();
                  _cache[key] = new CacheItem(value);
                  return value;
              }
      
              private struct CacheItem
              {
                  public CacheItem(TValue item)
                      : this()
                  {
                      Item = item;
                      CacheTime = DateTime.Now;
                  }
      
                  public TValue Item { get; private set; }
                  public DateTime CacheTime { get; private set; }
              }
      
          }
      }
      

      您可以将 lambda 表达式传递给 Get 方法,例如从数据库中检索值。

      【讨论】:

        【解决方案6】:

        您也许可以只使用一个简单的字典。使 Cache 在 Web 环境中如此特别的原因在于它可以持久存在并且以许多用户可以访问它的方式进行限定。在控制台应用程序中,您没有这些问题。如果您的需求足够简单,可以使用字典或类似结构来快速查找您从数据库中提取的值。

        【讨论】:

        • 实际上,缓存的特殊之处在于它处理过期的方式,而不是您提到的字典的唯一查找。
        【解决方案7】:

        这是我在控制台中使用的一个非常简单的缓存类,它具有自我清理和易于实现的功能。

        用法:

        return Cache.Get("MyCacheKey", 30, () => { return new Model.Guide().ChannelListings.BuildChannelList(); });
        

        班级:

            using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;
            using System.Timers;
        
            namespace MyAppNamespace
            {
                public static class Cache
                {
                    private static Timer cleanupTimer = new Timer() { AutoReset = true, Enabled = true, Interval = 60000 };
                    private static readonly Dictionary<string, CacheItem> internalCache = new Dictionary<string, CacheItem>();
        
                    static Cache()
                    {
                        cleanupTimer.Elapsed += Clean;
                        cleanupTimer.Start();
                    }
        
                    private static void Clean(object sender, ElapsedEventArgs e)
                    {
                        internalCache.Keys.ToList().ForEach(x => { try { if (internalCache[x].ExpireTime <= e.SignalTime) { Remove(x); } } catch (Exception) { /*swallow it*/ } });
                    }
        
                    public static T Get<T>(string key, int expiresMinutes, Func<T> refreshFunction)
                    {
                        if (internalCache.ContainsKey(key) && internalCache[key].ExpireTime > DateTime.Now)
                        {
                            return (T)internalCache[key].Item;
                        }
        
                        var result = refreshFunction();
        
                        Set(key, result, expiresMinutes);
        
                        return result;
                    }
        
                    public static void Set(string key, object item, int expiresMinutes)
                    {
                        Remove(key);
        
                        internalCache.Add(key, new CacheItem(item, expiresMinutes));
                    }
        
                    public static void Remove(string key)
                    {
                        if (internalCache.ContainsKey(key))
                        {
                            internalCache.Remove(key);
                        }
                    }
        
                    private struct CacheItem
                    {
                        public CacheItem(object item, int expiresMinutes)
                            : this()
                        {
                            Item = item;
                            ExpireTime = DateTime.Now.AddMinutes(expiresMinutes);
                        }
        
                        public object Item { get; private set; }
                        public DateTime ExpireTime { get; private set; }
                    }
                }
        
        }
        

        【讨论】:

        • 清理函数在指定时间间隔后被调用,如何再次调用该函数,应该更新一个新列表?
        猜你喜欢
        • 2015-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多