Cache.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. //
  2. // System.Web.Caching
  3. //
  4. // Author:
  5. // Patrik Torstensson
  6. // Daniel Cazzulino [DHC] ([email protected])
  7. //
  8. using System;
  9. using System.Collections;
  10. using System.Threading;
  11. namespace System.Web.Caching {
  12. public sealed class Cache : IEnumerable {
  13. public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
  14. public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
  15. // Helper objects
  16. private CacheExpires _objExpires;
  17. // The data storage
  18. private Hashtable _arrEntries;
  19. private ReaderWriterLock _lockEntries;
  20. private int _nItems;
  21. static private TimeSpan _datetimeOneYear = TimeSpan.FromDays (365);
  22. public Cache () {
  23. _nItems = 0;
  24. _lockEntries = new ReaderWriterLock ();
  25. _arrEntries = new Hashtable ();
  26. _objExpires = new CacheExpires (this);
  27. }
  28. /// <summary>
  29. /// Internal method to create a enumerator and over all public
  30. /// items in the cache and is used by GetEnumerator method.
  31. /// </summary>
  32. /// <returns>
  33. /// Returns IDictionaryEnumerator that contains all public items in the cache
  34. /// </returns>
  35. private IDictionaryEnumerator CreateEnumerator () {
  36. Hashtable objTable;
  37. //Locking with -1 provides a non-expiring lock.
  38. _lockEntries.AcquireReaderLock (-1);
  39. try {
  40. // Create a new hashtable to return as collection of public items
  41. objTable = new Hashtable (_arrEntries.Count);
  42. foreach (DictionaryEntry objEntry in _arrEntries) {
  43. if (objEntry.Key == null)
  44. continue;
  45. CacheEntry entry = (CacheEntry) objEntry.Value;
  46. if (entry.IsPublic)
  47. objTable.Add (objEntry.Key, entry.Item);
  48. }
  49. } finally {
  50. _lockEntries.ReleaseReaderLock ();
  51. }
  52. return objTable.GetEnumerator ();
  53. }
  54. IEnumerator IEnumerable.GetEnumerator () {
  55. return GetEnumerator ();
  56. }
  57. public IDictionaryEnumerator GetEnumerator () {
  58. return CreateEnumerator ();
  59. }
  60. internal void Touch(string strKey) {
  61. GetEntry (strKey);
  62. }
  63. /// <summary>
  64. /// Adds the specified item to the Cache object with
  65. /// dependencies, expiration and priority policies, and a
  66. /// delegate you can use to notify your application when the
  67. /// inserted item is removed from the Cache.
  68. /// </summary>
  69. /// <param name="strKey">The cache key used to reference the item.</param>
  70. /// <param name="objItem">The item to be added to the cache.</param>
  71. /// <param name="objDependency">
  72. /// The file or cache key dependencies for the item. When any
  73. /// dependency changes, the object becomes invalid and is removed
  74. /// from the cache. If there are no dependencies, this paramter
  75. /// contains a null reference.
  76. /// </param>
  77. /// <param name="absolutExpiration">
  78. /// The time at which the added object expires and is removed from the cache.
  79. /// </param>
  80. /// <param name="slidingExpiration">
  81. /// The interval between the time the added object was last
  82. /// accessed and when that object expires. If this value is the
  83. /// equivalent of 20 minutes, the object expires and is removed
  84. /// from the cache 20 minutes after it is last accessed.
  85. /// </param>
  86. /// <param name="enumPriority">
  87. /// The relative cost of the object, as expressed by the
  88. /// CacheItemPriority enumeration. The cache uses this value when
  89. /// it evicts objects; objects with a lower cost are removed from
  90. /// the cache before objects with a higher cost.
  91. /// </param>
  92. /// <param name="eventRemoveCallback">
  93. /// A delegate that, if provided, is called when an object is
  94. /// removed from the cache. You can use this to notify
  95. /// applications when their objects are deleted from the
  96. /// cache.
  97. /// </param>
  98. /// <returns>The Object item added to the Cache.</returns>
  99. public object Add (string strKey, object objItem, CacheDependency objDependency,
  100. DateTime absolutExpiration, TimeSpan slidingExpiration,
  101. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  102. return Add (strKey, objItem, objDependency, absolutExpiration,
  103. slidingExpiration, enumPriority, eventRemoveCallback, true, false);
  104. }
  105. private object Add (string strKey,
  106. object objItem,
  107. CacheDependency objDependency,
  108. DateTime absolutExpiration,
  109. TimeSpan slidingExpiration,
  110. CacheItemPriority enumPriority,
  111. CacheItemRemovedCallback eventRemoveCallback,
  112. bool pub,
  113. bool overwrite) {
  114. if (strKey == null)
  115. throw new ArgumentNullException ("strKey");
  116. if (objItem == null)
  117. throw new ArgumentNullException ("objItem");
  118. if (slidingExpiration > _datetimeOneYear)
  119. throw new ArgumentOutOfRangeException ("slidingExpiration");
  120. CacheEntry objEntry;
  121. CacheEntry objOldEntry = null;
  122. long longHitRange = 10000;
  123. // todo: check decay and make up the minHit range
  124. objEntry = new CacheEntry (this,
  125. strKey,
  126. objItem,
  127. objDependency,
  128. eventRemoveCallback,
  129. absolutExpiration,
  130. slidingExpiration,
  131. longHitRange,
  132. pub,
  133. enumPriority);
  134. Interlocked.Increment (ref _nItems);
  135. _lockEntries.AcquireWriterLock (-1);
  136. try {
  137. if (_arrEntries.Contains (strKey)) {
  138. if (overwrite)
  139. objOldEntry = _arrEntries [strKey] as CacheEntry;
  140. else
  141. return null;
  142. }
  143. objEntry.Hit ();
  144. _arrEntries [strKey] = objEntry;
  145. } finally {
  146. _lockEntries.ReleaseLock ();
  147. }
  148. if (objOldEntry != null) {
  149. if (objOldEntry.HasAbsoluteExpiration || objOldEntry.HasSlidingExpiration)
  150. _objExpires.Remove (objOldEntry);
  151. objOldEntry.Close (CacheItemRemovedReason.Removed);
  152. }
  153. // If we have any kind of expiration add into the CacheExpires class
  154. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  155. if (objEntry.HasSlidingExpiration)
  156. objEntry.Expires = DateTime.Now.Ticks + objEntry.SlidingExpiration;
  157. _objExpires.Add (objEntry);
  158. }
  159. return objEntry.Item;
  160. }
  161. /// <summary>
  162. /// Inserts an item into the Cache object with a cache key to
  163. /// reference its location and using default values provided by
  164. /// the CacheItemPriority enumeration.
  165. /// </summary>
  166. /// <param name="strKey">The cache key used to reference the item.</param>
  167. /// <param name="objItem">The item to be added to the cache.</param>
  168. public void Insert (string strKey, object objItem) {
  169. Add (strKey, objItem, null, NoAbsoluteExpiration, NoSlidingExpiration,
  170. CacheItemPriority.Default, null, true, true);
  171. }
  172. /// <summary>
  173. /// Inserts an object into the Cache that has file or key dependencies.
  174. /// </summary>
  175. /// <param name="strKey">The cache key used to reference the item.</param>
  176. /// <param name="objItem">The item to be added to the cache.</param>
  177. /// <param name="objDependency">
  178. /// The file or cache key dependencies for the item. When any
  179. /// dependency changes, the object becomes invalid and is removed
  180. /// from the cache. If there are no dependencies, this paramter
  181. /// contains a null reference.
  182. /// </param>
  183. public void Insert (string strKey, object objItem, CacheDependency objDependency) {
  184. Add (strKey, objItem, objDependency, NoAbsoluteExpiration, NoSlidingExpiration,
  185. CacheItemPriority.Default, null, true, true);
  186. }
  187. /// <summary>
  188. /// Inserts an object into the Cache with dependencies and expiration policies.
  189. /// </summary>
  190. /// <param name="strKey">The cache key used to reference the item.</param>
  191. /// <param name="objItem">The item to be added to the cache.</param>
  192. /// <param name="objDependency">
  193. /// The file or cache key dependencies for the item. When any
  194. /// dependency changes, the object becomes invalid and is removed
  195. /// from the cache. If there are no dependencies, this paramter
  196. /// contains a null reference.
  197. /// </param>
  198. /// <param name="absolutExpiration">
  199. /// The time at which the added object expires and is removed from the cache.
  200. /// </param>
  201. /// <param name="slidingExpiration">The interval between the
  202. /// time the added object was last accessed and when that object
  203. /// expires. If this value is the equivalent of 20 minutes, the
  204. /// object expires and is removed from the cache 20 minutes after
  205. /// it is last accessed.
  206. /// </param>
  207. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  208. DateTime absolutExpiration, TimeSpan slidingExpiration) {
  209. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  210. CacheItemPriority.Default, null, true, true);
  211. }
  212. /// <summary>
  213. /// Inserts an object into the Cache object with dependencies,
  214. /// expiration and priority policies, and a delegate you can use
  215. /// to notify your application when the inserted item is removed
  216. /// from the Cache.
  217. /// </summary>
  218. /// <param name="strKey">The cache key used to reference the item.</param>
  219. /// <param name="objItem">The item to be added to the cache.</param>
  220. /// <param name="objDependency">
  221. /// The file or cache key dependencies for the item. When any
  222. /// dependency changes, the object becomes invalid and is removed
  223. /// from the cache. If there are no dependencies, this paramter
  224. /// contains a null reference.
  225. /// </param>
  226. /// <param name="absolutExpiration">
  227. /// The time at which the added object expires and is removed from the cache.
  228. /// </param>
  229. /// <param name="slidingExpiration">
  230. /// The interval between the time the added object was last
  231. /// accessed and when that object expires. If this value is the
  232. /// equivalent of 20 minutes, the object expires and is removed
  233. /// from the cache 20 minutes after it is last accessed.
  234. /// </param>
  235. /// <param name="enumPriority">The relative cost of the object,
  236. /// as expressed by the CacheItemPriority enumeration. The cache
  237. /// uses this value when it evicts objects; objects with a lower
  238. /// cost are removed from the cache before objects with a higher
  239. /// cost.
  240. /// </param>
  241. /// <param name="eventRemoveCallback">A delegate that, if
  242. /// provided, is called when an object is removed from the cache.
  243. /// You can use this to notify applications when their objects
  244. /// are deleted from the cache.
  245. /// </param>
  246. public void Insert (string strKey, object objItem, CacheDependency objDependency,
  247. DateTime absolutExpiration, TimeSpan slidingExpiration,
  248. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  249. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  250. enumPriority, eventRemoveCallback, true, true);
  251. }
  252. // Called from other internal System.Web methods to add non-public objects into
  253. // cache, like output cache etc
  254. internal void InsertPrivate (string strKey, object objItem, CacheDependency objDependency,
  255. DateTime absolutExpiration, TimeSpan slidingExpiration,
  256. CacheItemPriority enumPriority, CacheItemRemovedCallback eventRemoveCallback) {
  257. Add (strKey, objItem, objDependency, absolutExpiration, slidingExpiration,
  258. enumPriority, eventRemoveCallback, false, true);
  259. }
  260. /// <summary>
  261. /// Removes the specified item from the Cache object.
  262. /// </summary>
  263. /// <param name="strKey">The cache key for the cache item to remove.</param>
  264. /// <returns>
  265. /// The item removed from the Cache. If the value in the key
  266. /// parameter is not found, returns a null reference.
  267. /// </returns>
  268. public object Remove (string strKey) {
  269. return Remove (strKey, CacheItemRemovedReason.Removed);
  270. }
  271. /// <summary>
  272. /// Internal method that updates the cache, decremenents the
  273. /// number of existing items and call close on the cache entry.
  274. /// This method is also used from the ExpiresBuckets class to
  275. /// remove an item during GC flush.
  276. /// </summary>
  277. /// <param name="strKey">The cache key for the cache item to remove.</param>
  278. /// <param name="enumReason">Reason why the item is removed.</param>
  279. /// <returns>
  280. /// The item removed from the Cache. If the value in the key
  281. /// parameter is not found, returns a null reference.
  282. /// </returns>
  283. internal object Remove (string strKey, CacheItemRemovedReason enumReason) {
  284. CacheEntry objEntry = null;
  285. if (strKey == null)
  286. throw new ArgumentNullException ("strKey");
  287. _lockEntries.AcquireWriterLock (-1);
  288. try {
  289. objEntry = _arrEntries [strKey] as CacheEntry;
  290. if (null == objEntry)
  291. return null;
  292. _arrEntries.Remove (strKey);
  293. }
  294. finally {
  295. _lockEntries.ReleaseWriterLock ();
  296. }
  297. if (objEntry.HasAbsoluteExpiration || objEntry.HasSlidingExpiration)
  298. _objExpires.Remove (objEntry);
  299. objEntry.Close (enumReason);
  300. Interlocked.Decrement (ref _nItems);
  301. return objEntry.Item;
  302. }
  303. /// <summary>
  304. /// Retrieves the specified item from the Cache object.
  305. /// </summary>
  306. /// <param name="strKey">The identifier for the cache item to retrieve.</param>
  307. /// <returns>The retrieved cache item, or a null reference.</returns>
  308. public object Get (string strKey) {
  309. CacheEntry objEntry = GetEntry (strKey);
  310. if (objEntry == null)
  311. return null;
  312. return objEntry.Item;
  313. }
  314. internal CacheEntry GetEntry (string strKey) {
  315. CacheEntry objEntry = null;
  316. long ticksNow = DateTime.Now.Ticks;
  317. if (strKey == null)
  318. throw new ArgumentNullException ("strKey");
  319. _lockEntries.AcquireReaderLock (-1);
  320. try {
  321. objEntry = _arrEntries [strKey] as CacheEntry;
  322. if (null == objEntry)
  323. return null;
  324. }
  325. finally {
  326. _lockEntries.ReleaseReaderLock ();
  327. }
  328. if (objEntry.HasSlidingExpiration || objEntry.HasAbsoluteExpiration) {
  329. if (objEntry.Expires < ticksNow) {
  330. Remove (strKey, CacheItemRemovedReason.Expired);
  331. return null;
  332. }
  333. }
  334. objEntry.Hit ();
  335. if (objEntry.HasSlidingExpiration) {
  336. long ticksExpires = ticksNow + objEntry.SlidingExpiration;
  337. _objExpires.Update (objEntry, ticksExpires);
  338. objEntry.Expires = ticksExpires;
  339. }
  340. return objEntry;
  341. }
  342. /// <summary>
  343. /// Gets the number of items stored in the cache.
  344. /// </summary>
  345. public int Count {
  346. get { return _nItems; }
  347. }
  348. /// <summary>
  349. /// Gets or sets the cache item at the specified key.
  350. /// </summary>
  351. public object this [string strKey] {
  352. get {
  353. return Get (strKey);
  354. }
  355. set {
  356. Insert (strKey, value);
  357. }
  358. }
  359. }
  360. }