2 // System.Web.Caching.Cache
5 // Lluis Sanchez (lluis@ximian.com)
7 // (C) 2005 Novell, Inc (http://www.novell.com)
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Threading;
31 using System.Collections;
32 using System.Security.Permissions;
34 using System.Collections.Generic;
37 namespace System.Web.Caching
39 // CAS - no InheritanceDemand here as the class is sealed
40 [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
41 public sealed class Cache: IEnumerable
44 Dictionary <string, CacheItem> cache;
48 Cache dependencyCache;
49 public static readonly DateTime NoAbsoluteExpiration = DateTime.MaxValue;
50 public static readonly TimeSpan NoSlidingExpiration = TimeSpan.Zero;
55 cache = new Dictionary <string, CacheItem> ();
57 cache = new Hashtable ();
62 get { return cache.Count; }
65 public object this [string key] {
66 get { return Get (key); }
67 set { Insert (key, value); }
70 public object Add (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
73 throw new ArgumentNullException ("key");
79 cache.TryGetValue (key, out it);
81 it = (CacheItem) cache [key];
85 Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, priority, onRemoveCallback, false);
90 public object Get (string key)
95 if (!cache.TryGetValue (key, out it))
98 it = (CacheItem) cache [key];
103 if (it.Dependency != null && it.Dependency.HasChanged) {
104 Remove (it.Key, CacheItemRemovedReason.DependencyChanged, false);
108 if (it.SlidingExpiration != NoSlidingExpiration) {
109 it.AbsoluteExpiration = DateTime.Now + it.SlidingExpiration;
110 it.Timer.Change ((int)it.SlidingExpiration.TotalMilliseconds, Timeout.Infinite);
111 } else if (DateTime.Now >= it.AbsoluteExpiration) {
112 Remove (key, CacheItemRemovedReason.Expired, false);
120 public void Insert (string key, object value)
122 Insert (key, value, null, NoAbsoluteExpiration, NoSlidingExpiration, CacheItemPriority.Normal, null, true);
125 public void Insert (string key, object value, CacheDependency dependencies)
127 Insert (key, value, dependencies, NoAbsoluteExpiration, NoSlidingExpiration, CacheItemPriority.Normal, null, true);
130 public void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration)
132 Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, null, true);
135 public void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration,
136 CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback)
138 Insert (key, value, dependencies, absoluteExpiration, slidingExpiration, CacheItemPriority.Normal, onRemoveCallback, true);
141 void Insert (string key, object value, CacheDependency dependencies, DateTime absoluteExpiration, TimeSpan slidingExpiration,
142 CacheItemPriority priority, CacheItemRemovedCallback onRemoveCallback, bool doLock)
145 throw new ArgumentNullException ("key");
147 throw new ArgumentNullException ("value");
148 if (slidingExpiration < TimeSpan.Zero || slidingExpiration > TimeSpan.FromDays (365))
149 throw new ArgumentNullException ("slidingExpiration");
150 if (absoluteExpiration != NoAbsoluteExpiration && slidingExpiration != NoSlidingExpiration)
151 throw new ArgumentException ("Both absoluteExpiration and slidingExpiration are specified");
153 CacheItem ci = new CacheItem ();
157 if (dependencies != null) {
158 ci.Dependency = dependencies;
159 dependencies.DependencyChanged += new EventHandler (OnDependencyChanged);
160 dependencies.SetCache (DependencyCache);
163 ci.Priority = priority;
164 SetItemTimeout (ci, absoluteExpiration, slidingExpiration, onRemoveCallback, key, doLock);
167 internal void SetItemTimeout (string key, DateTime absoluteExpiration, TimeSpan slidingExpiration, bool doLock)
173 Monitor.Enter (cache);
175 cache.TryGetValue (key, out ci);
177 ci = (CacheItem) cache [key];
181 SetItemTimeout (ci, absoluteExpiration, slidingExpiration, ci.OnRemoveCallback, null, false);
184 Monitor.Exit (cache);
188 void SetItemTimeout (CacheItem ci, DateTime absoluteExpiration, TimeSpan slidingExpiration, CacheItemRemovedCallback onRemoveCallback,
189 string key, bool doLock)
191 ci.SlidingExpiration = slidingExpiration;
192 if (slidingExpiration != NoSlidingExpiration)
193 ci.AbsoluteExpiration = DateTime.Now + slidingExpiration;
195 ci.AbsoluteExpiration = absoluteExpiration;
197 ci.OnRemoveCallback = onRemoveCallback;
201 Monitor.Enter (cache);
203 if (ci.Timer != null) {
211 ci.LastChange = DateTime.Now;
212 if (ci.AbsoluteExpiration != NoAbsoluteExpiration) {
213 int remaining = Math.Max (0, (int)(ci.AbsoluteExpiration - DateTime.Now).TotalMilliseconds);
214 ci.Timer = new Timer (new TimerCallback (ItemExpired), ci, remaining, Timeout.Infinite);
218 Monitor.Exit (cache);
222 public object Remove (string key)
224 return Remove (key, CacheItemRemovedReason.Removed, true);
227 object Remove (string key, CacheItemRemovedReason reason, bool doLock)
233 Monitor.Enter (cache);
235 cache.TryGetValue (key, out it);
237 it = (CacheItem) cache [key];
242 Monitor.Exit (cache);
250 if (it.Dependency != null) {
252 it.Dependency.SetCache (null);
254 it.Dependency.DependencyChanged -= new EventHandler (OnDependencyChanged);
255 it.Dependency.Dispose ();
257 if (it.OnRemoveCallback != null) {
259 it.OnRemoveCallback (key, it.Value, reason);
261 //TODO: anything to be done here?
269 // Used when shutting down the application so that
270 // session_end events are sent for all sessions.
271 internal void InvokePrivateCallbacks ()
273 CacheItemRemovedReason reason = CacheItemRemovedReason.Removed;
275 foreach (string key in cache.Keys) {
278 cache.TryGetValue (key, out item);
280 item = (CacheItem) cache [key];
283 if (item != null && item.OnRemoveCallback != null) {
285 item.OnRemoveCallback (key, item.Value, reason);
287 //TODO: anything to be done here?
294 public IDictionaryEnumerator GetEnumerator ()
296 ArrayList list = new ArrayList ();
299 foreach (CacheItem it in cache.Values)
302 list.AddRange (cache.Values);
305 return new CacheItemEnumerator (list);
308 IEnumerator IEnumerable.GetEnumerator ()
310 return GetEnumerator ();
313 void OnDependencyChanged (object o, EventArgs a)
315 CheckDependencies ();
318 void ItemExpired(object cacheItem) {
319 CacheItem ci = (CacheItem)cacheItem;
323 Remove (ci.Key, CacheItemRemovedReason.Expired, true);
326 internal void CheckDependencies ()
330 list = new ArrayList ();
332 foreach (CacheItem it in cache.Values)
335 list.AddRange (cache.Values);
338 foreach (CacheItem it in list) {
339 if (it.Dependency != null && it.Dependency.HasChanged)
340 Remove (it.Key, CacheItemRemovedReason.DependencyChanged, false);
345 internal DateTime GetKeyLastChange (string key)
350 if (!cache.TryGetValue (key, out it))
351 return DateTime.MaxValue;
353 it = (CacheItem) cache [key];
356 return DateTime.MaxValue;
358 return it.LastChange;
362 internal Cache DependencyCache {
364 if (dependencyCache == null)
367 return dependencyCache;
369 set { dependencyCache = value; }
377 public CacheDependency Dependency;
378 public DateTime AbsoluteExpiration;
379 public TimeSpan SlidingExpiration;
380 public CacheItemPriority Priority;
381 public CacheItemRemovedCallback OnRemoveCallback;
382 public DateTime LastChange;
386 class CacheItemEnumerator: IDictionaryEnumerator
391 public CacheItemEnumerator (ArrayList list)
398 if (pos < 0 || pos >= list.Count)
399 throw new InvalidOperationException ();
400 return (CacheItem) list [pos];
404 public DictionaryEntry Entry {
405 get { return new DictionaryEntry (Item.Key, Item.Value); }
409 get { return Item.Key; }
412 public object Value {
413 get { return Item.Value; }
416 public object Current {
417 get { return Entry; }
420 public bool MoveNext ()
422 return (++pos < list.Count);