2 using System
.Collections
;
3 using System
.Collections
.Generic
;
4 using System
.Configuration
.Provider
;
5 using System
.Runtime
.Serialization
;
6 using System
.Security
.Permissions
;
7 using System
.Threading
;
9 using System
.Web
.Compilation
;
10 using System
.Web
.Configuration
;
11 using System
.Web
.Hosting
;
12 using System
.Web
.Management
;
14 using System
.Web
.Util
;
16 using System
.Runtime
.Serialization
.Formatters
.Binary
;
18 namespace System
.Web
.Caching
{
21 We currently have the following buffer types:
23 HttpResponseBufferElement - managed memory
24 HttpResponseUnmanagedBufferElement - native memory
25 HttpResourceResponseElement - pointer to resource
26 HttpFileResponseElement - contains a file name, file can have 64-bit length
27 HttpSubstBlockResponseElement - subst callback
29 Custom output cache providers do not support all features. If we cannot use a custom output cache,
30 provider, we will use the internal cache. We will use a custom output cache provider only if:
32 a) no instance validation callbacks (static callbacks are okay)
33 b) no dependencies other than files
34 c) no sliding expiration
35 d) no instance substitution callbacks (HttpSubstBlockResponseElement) (static callbacks okay)
40 The custom cache provider receives an OutputCacheEntry, which contains the status, headers, response elements,
41 cache policy settings, and kernel cache key. The cache provider can coallesce response elements. For example,
42 a disk based cache may return a single response element which is the name of a file containing the entire response.
46 If the custom cache provider is distributed, the cache keys must be unique for two applications with the same name
47 but different domains. For example, a request to http://company1.com/myapp/page.aspx and a request to
48 http://company2.com/myapp/page.aspx need to use output cache entry keys that include the domain name. Today, the
49 key is just /myapp/page.aspx. It would need to include the Host header, but if that is "localhost", "::1", etc,
50 we'd need to use %USERDOMAIN%\%COMPUTERNAME% instead. But it is possible for a single site to have multiple host name
51 bindings, so this would result in multiple cache entries for potentially the same cached result. We could allow this to be
52 adjusted by adding a setting to the cache policy.
56 internal class DependencyCacheEntry
{
57 private string _providerName
;
58 private string _outputCacheEntryKey
;
59 private string _kernelCacheEntryKey
;
61 internal string ProviderName { get { return _providerName; }
}
62 internal string OutputCacheEntryKey { get { return _outputCacheEntryKey; }
}
63 internal string KernelCacheEntryKey { get { return _kernelCacheEntryKey; }
}
65 internal DependencyCacheEntry(string oceKey
, string kernelCacheEntryKey
, string providerName
) {
66 _outputCacheEntryKey
= oceKey
;
67 _kernelCacheEntryKey
= kernelCacheEntryKey
;
68 _providerName
= providerName
;
72 public static class OutputCache
{
73 private const string OUTPUTCACHE_KEYPREFIX_DEPENDENCIES
= CacheInternal
.PrefixOutputCache
+ "D";
74 internal const string ASPNET_INTERNAL_PROVIDER_NAME
= "AspNetInternalProvider";
75 private static bool s_inited
;
76 private static object s_initLock
= new object();
77 private static CacheItemRemovedCallback s_entryRemovedCallback
;
78 private static CacheItemRemovedCallback s_dependencyRemovedCallback
;
79 private static CacheItemRemovedCallback s_dependencyRemovedCallbackForFragment
;
80 private static OutputCacheProvider s_defaultProvider
;
81 private static OutputCacheProviderCollection s_providers
;
82 // when there are no providers being used, we'll use this count to optimize performance when the value is zero.
83 private static int s_cEntries
;
86 // private helper methods
89 private static void AddCacheKeyToDependencies(ref CacheDependency dependencies
, string cacheKey
) {
90 CacheDependency keyDep
= new CacheDependency(0, null, new string[1] {cacheKey}
);
91 if (dependencies
== null) {
92 dependencies
= keyDep
;
95 // if it's not an aggregate, we have to create one because you can't add
96 // it to anything but an aggregate
97 AggregateCacheDependency agg
= dependencies
as AggregateCacheDependency
;
102 agg
= new AggregateCacheDependency();
103 agg
.Add(keyDep
, dependencies
);
109 private static void EnsureInitialized() {
115 OutputCacheSection settings
= RuntimeConfig
.GetAppConfig().OutputCache
;
116 s_providers
= settings
.CreateProviderCollection();
117 s_defaultProvider
= settings
.GetDefaultProvider(s_providers
);
118 s_entryRemovedCallback
= new CacheItemRemovedCallback(OutputCache
.EntryRemovedCallback
);
119 s_dependencyRemovedCallback
= new CacheItemRemovedCallback(OutputCache
.DependencyRemovedCallback
);
120 s_dependencyRemovedCallbackForFragment
= new CacheItemRemovedCallback(OutputCache
.DependencyRemovedCallbackForFragment
);
126 private static void DecrementCount() {
127 if (Providers
== null)
128 Interlocked
.Decrement(ref s_cEntries
);
131 private static void IncrementCount() {
132 if (Providers
== null)
133 Interlocked
.Increment(ref s_cEntries
);
136 private static OutputCacheProvider
GetFragmentProvider(String providerName
) {
137 // if providerName is null, use default provider. If default provider is null, we'll use internal cache.
138 // if providerName is not null, get it from the provider collection.
139 OutputCacheProvider provider
= null;
140 if (providerName
== null) {
141 provider
= s_defaultProvider
;
144 provider
= s_providers
[providerName
];
145 if (provider
== null) {
146 Debug
.Assert(false, "Unexpected, " + providerName
+ " should be a member of the collection.");
147 throw new ProviderException(SR
.GetString(SR
.Provider_Not_Found
, providerName
));
151 string msg
= (provider
!= null) ? provider
.GetType().Name
: "null";
152 Debug
.Trace("OutputCache", "GetFragmentProvider(" + providerName
+ ") --> " + msg
);
157 private static OutputCacheProvider
GetProvider(HttpContext context
) {
158 Debug
.Assert(context
!= null, "context != null");
159 if (context
== null) {
162 // Call GetOutputCacheProviderName
163 // so it can determine which provider to use.
164 HttpApplication app
= context
.ApplicationInstance
;
165 string name
= app
.GetOutputCacheProviderName(context
);
167 throw new ProviderException(SR
.GetString(SR
.GetOutputCacheProviderName_Invalid
, name
));
169 // AspNetInternalProvider means use the internal cache
170 if (name
== OutputCache
.ASPNET_INTERNAL_PROVIDER_NAME
) {
173 OutputCacheProvider provider
= (s_providers
== null) ? null : s_providers
[name
];
174 if (provider
== null) {
175 throw new ProviderException(SR
.GetString(SR
.GetOutputCacheProviderName_Invalid
, name
));
180 private static OutputCacheEntry
Convert(CachedRawResponse cachedRawResponse
, string depKey
, string[] fileDependencies
) {
181 List
<HeaderElement
> headerElements
= null;
182 ArrayList headers
= cachedRawResponse
._rawResponse
.Headers
;
183 int count
= (headers
!= null) ? headers
.Count
: 0;
184 for (int i
= 0; i
< count
; i
++) {
185 if (headerElements
== null) {
186 headerElements
= new List
<HeaderElement
>(count
);
188 HttpResponseHeader h
= (HttpResponseHeader
)(headers
[i
]);
189 headerElements
.Add(new HeaderElement(h
.Name
, h
.Value
));
192 List
<ResponseElement
> responseElements
= null;
193 ArrayList buffers
= cachedRawResponse
._rawResponse
.Buffers
;
194 count
= (buffers
!= null) ? buffers
.Count
: 0;
195 for (int i
= 0; i
< count
; i
++) {
196 if (responseElements
== null) {
197 responseElements
= new List
<ResponseElement
>(count
);
199 IHttpResponseElement elem
= buffers
[i
] as IHttpResponseElement
;
200 if (elem
is HttpFileResponseElement
) {
201 HttpFileResponseElement fileElement
= elem
as HttpFileResponseElement
;
202 responseElements
.Add(new FileResponseElement(fileElement
.FileName
, fileElement
.Offset
, elem
.GetSize()));
204 else if (elem
is HttpSubstBlockResponseElement
) {
205 HttpSubstBlockResponseElement substElement
= elem
as HttpSubstBlockResponseElement
;
206 responseElements
.Add(new SubstitutionResponseElement(substElement
.Callback
));
209 byte[] b
= elem
.GetBytes();
210 long length
= (b
!= null) ? b
.Length
: 0;
211 responseElements
.Add(new MemoryResponseElement(b
, length
));
215 OutputCacheEntry oce
= new OutputCacheEntry(
216 cachedRawResponse
._cachedVaryId
,
217 cachedRawResponse
._settings
,
218 cachedRawResponse
._kernelCacheUrl
,
221 cachedRawResponse
._rawResponse
.StatusCode
,
222 cachedRawResponse
._rawResponse
.StatusDescription
,
230 private static CachedRawResponse
Convert(OutputCacheEntry oce
) {
231 ArrayList headers
= null;
232 if (oce
.HeaderElements
!= null && oce
.HeaderElements
.Count
> 0) {
233 headers
= new ArrayList(oce
.HeaderElements
.Count
);
234 for (int i
= 0; i
< oce
.HeaderElements
.Count
; i
++) {
235 HttpResponseHeader h
= new HttpResponseHeader(oce
.HeaderElements
[i
].Name
, oce
.HeaderElements
[i
].Value
);
240 ArrayList buffers
= null;
241 if (oce
.ResponseElements
!= null && oce
.ResponseElements
.Count
> 0) {
242 buffers
= new ArrayList(oce
.ResponseElements
.Count
);
243 for (int i
= 0; i
< oce
.ResponseElements
.Count
; i
++) {
244 ResponseElement re
= oce
.ResponseElements
[i
];
245 IHttpResponseElement elem
= null;
246 if (re
is FileResponseElement
) {
247 HttpContext context
= HttpContext
.Current
;
248 HttpWorkerRequest wr
= (context
!= null) ? context
.WorkerRequest
: null;
249 bool supportsLongTransmitFile
= (wr
!= null && wr
.SupportsLongTransmitFile
);
250 bool isImpersonating
= ((context
!= null && context
.IsClientImpersonationConfigured
) || HttpRuntime
.IsOnUNCShareInternal
);
251 FileResponseElement fre
= (FileResponseElement
)re
;
253 // DevDiv #21203: Need to verify permission to access the requested file since handled by native code.
254 HttpRuntime
.CheckFilePermission(fre
.Path
);
256 elem
= new HttpFileResponseElement(fre
.Path
, fre
.Offset
, fre
.Length
, isImpersonating
, supportsLongTransmitFile
);
258 else if (re
is MemoryResponseElement
) {
259 MemoryResponseElement mre
= (MemoryResponseElement
)re
;
260 int size
= System
.Convert
.ToInt32(mre
.Length
);
261 elem
= new HttpResponseBufferElement(mre
.Buffer
, size
);
263 else if (re
is SubstitutionResponseElement
) {
264 SubstitutionResponseElement sre
= (SubstitutionResponseElement
)re
;
265 elem
= new HttpSubstBlockResponseElement(sre
.Callback
);
268 throw new NotSupportedException();
274 buffers
= new ArrayList();
277 HttpRawResponse rawResponse
= new HttpRawResponse(oce
.StatusCode
, oce
.StatusDescription
, headers
, buffers
, false /*hasSubstBlocks*/);
278 CachedRawResponse cachedRawResponse
= new CachedRawResponse(rawResponse
, oce
.Settings
, oce
.KernelCacheUrl
, oce
.CachedVaryId
);
280 return cachedRawResponse
;
285 // helpers for accessing InternalCache
289 private static CachedVary
UtcAdd(String key
, CachedVary cachedVary
) {
290 return (CachedVary
) HttpRuntime
.Cache
.InternalCache
.Add(key
, cachedVary
, null);
293 // add ControlCachedVary
294 private static ControlCachedVary
UtcAdd(String key
, ControlCachedVary cachedVary
) {
295 return (ControlCachedVary
) HttpRuntime
.Cache
.InternalCache
.Add(key
, cachedVary
, null);
298 private static bool IsSubstBlockSerializable(HttpRawResponse rawResponse
) {
299 if (!rawResponse
.HasSubstBlocks
)
301 for (int i
= 0; i
< rawResponse
.Buffers
.Count
; i
++) {
302 HttpSubstBlockResponseElement substBlock
= rawResponse
.Buffers
[i
] as HttpSubstBlockResponseElement
;
303 if (substBlock
== null)
305 if (!substBlock
.Callback
.Method
.IsStatic
)
316 private static void HandleErrorWithoutContext(Exception e
) {
317 HttpApplicationFactory
.RaiseError(e
);
319 WebBaseEvent
.RaiseRuntimeError(e
, typeof(OutputCache
));
325 // only used by providers
326 private static void DependencyRemovedCallback(string key
, object value, CacheItemRemovedReason reason
) {
327 Debug
.Trace("OutputCache", "DependencyRemovedCallback: reason=" + reason
+ ", key=" + key
);
329 DependencyCacheEntry dce
= value as DependencyCacheEntry
;
330 if (dce
.KernelCacheEntryKey
!= null) {
331 // invalidate kernel cache entry
332 if (HttpRuntime
.UseIntegratedPipeline
) {
333 UnsafeIISMethods
.MgdFlushKernelCache(dce
.KernelCacheEntryKey
);
336 UnsafeNativeMethods
.InvalidateKernelCache(dce
.KernelCacheEntryKey
);
339 if (reason
== CacheItemRemovedReason
.DependencyChanged
) {
340 if (dce
.OutputCacheEntryKey
!= null) {
342 OutputCache
.RemoveFromProvider(dce
.OutputCacheEntryKey
, dce
.ProviderName
);
344 catch (Exception e
) {
345 HandleErrorWithoutContext(e
);
351 // only used by providers
352 private static void DependencyRemovedCallbackForFragment(string key
, object value, CacheItemRemovedReason reason
) {
353 Debug
.Trace("OutputCache", "DependencyRemovedCallbackForFragment: reason=" + reason
+ ", key=" + key
);
355 if (reason
== CacheItemRemovedReason
.DependencyChanged
) {
356 DependencyCacheEntry dce
= value as DependencyCacheEntry
;
357 if (dce
.OutputCacheEntryKey
!= null) {
359 OutputCache
.RemoveFragment(dce
.OutputCacheEntryKey
, dce
.ProviderName
);
361 catch (Exception e
) {
362 HandleErrorWithoutContext(e
);
368 // only used by internal cache
369 private static void EntryRemovedCallback(string key
, object value, CacheItemRemovedReason reason
) {
370 Debug
.Trace("OutputCache", "EntryRemovedCallback: reason=" + reason
+ ", key=" + key
);
374 PerfCounters
.DecrementCounter(AppPerfCounter
.OUTPUT_CACHE_ENTRIES
);
375 PerfCounters
.IncrementCounter(AppPerfCounter
.OUTPUT_CACHE_TURNOVER_RATE
);
377 CachedRawResponse cachedRawResponse
= value as CachedRawResponse
;
378 if (cachedRawResponse
!= null) {
379 String kernelCacheUrl
= cachedRawResponse
._kernelCacheUrl
;
380 // if it is kernel cached, the url will be non-null.
381 // if the entry was re-inserted, don't remove kernel entry since it will be updated
382 if (kernelCacheUrl
!= null && HttpRuntime
.Cache
.InternalCache
.Get(key
) == null) {
383 // invalidate kernel cache entry
384 if (HttpRuntime
.UseIntegratedPipeline
) {
385 UnsafeIISMethods
.MgdFlushKernelCache(kernelCacheUrl
);
388 UnsafeNativeMethods
.InvalidateKernelCache(kernelCacheUrl
);
398 public static string DefaultProviderName
{
401 return (s_defaultProvider
!= null) ? s_defaultProvider
.Name
: OutputCache
.ASPNET_INTERNAL_PROVIDER_NAME
;
405 public static OutputCacheProviderCollection Providers
{
414 // internal properties and methods
419 // If we're not using a provider, we can optimize this so the OutputCacheModule
420 // only need run when there are entries--return true iff count is non-zero.
421 // If we're using a provider, it's not easy to keep track of the number of entries,
422 // so always return true (so OutputCacheModule always runs).
424 internal static bool InUse
{
426 return (Providers
== null) ? (s_cEntries
!= 0) : true;
430 internal static void ThrowIfProviderNotFound(String providerName
) {
431 // null means use default provider or internal cache
432 if (providerName
== null) {
435 OutputCacheProviderCollection providers
= Providers
;
437 if (providers
== null || providers
[providerName
] == null) {
438 throw new ProviderException(SR
.GetString(SR
.Provider_Not_Found
, providerName
));
442 internal static bool HasDependencyChanged(bool isFragment
, string depKey
, string[] fileDeps
, string kernelKey
, string oceKey
, string providerName
) {
447 Debug
.Trace("OutputCache", "HasDependencyChanged(" + depKey
+ ", ..., " + oceKey
+ ", ...) --> false");
452 // is the file dependency already in the in-memory cache?
453 if (HttpRuntime
.Cache
.InternalCache
.Get(depKey
) != null) {
455 Debug
.Trace("OutputCache", "HasDependencyChanged(" + depKey
+ ", ..., " + oceKey
+ ", ...) --> false");
460 // deserialize the file dependencies
461 CacheDependency dep
= new CacheDependency(0, fileDeps
);
463 int idStartIndex
= OUTPUTCACHE_KEYPREFIX_DEPENDENCIES
.Length
;
464 int idLength
= depKey
.Length
- idStartIndex
;
466 CacheItemRemovedCallback callback
= (isFragment
) ? s_dependencyRemovedCallbackForFragment
: s_dependencyRemovedCallback
;
468 // have the file dependencies changed?
469 if (String
.Compare(dep
.GetUniqueID(), 0, depKey
, idStartIndex
, idLength
, StringComparison
.Ordinal
) == 0) {
470 // file dependencies have not changed--cache them with callback to remove OutputCacheEntry if they change
471 HttpRuntime
.Cache
.InternalCache
.Insert(depKey
, new DependencyCacheEntry(oceKey
, kernelKey
, providerName
), new CacheInsertOptions() {
473 OnRemovedCallback
= callback
476 Debug
.Trace("OutputCache", "HasDependencyChanged(" + depKey
+ ", ..., " + oceKey
+ ", ...) --> false, DEPENDENCY RE-INSERTED");
481 // file dependencies have changed
484 Debug
.Trace("OutputCache", "HasDependencyChanged(" + depKey
+ ", ..., " + oceKey
+ ", ...) --> true, " + dep
.GetUniqueID());
490 [SecurityPermission(SecurityAction
.Assert
, Flags
= SecurityPermissionFlag
.SerializationFormatter
)]
491 public static void Serialize(Stream stream
, Object data
) {
492 BinaryFormatter formatter
= new BinaryFormatter();
493 if (data
is OutputCacheEntry
|| data
is PartialCachingCacheEntry
|| data
is CachedVary
|| data
is ControlCachedVary
||
494 data
is FileResponseElement
|| data
is MemoryResponseElement
|| data
is SubstitutionResponseElement
) {
495 formatter
.Serialize(stream
, data
);
498 throw new ArgumentException(SR
.GetString(SR
.OutputCacheExtensibility_CantSerializeDeserializeType
));
502 [SecurityPermission(SecurityAction
.Assert
, Flags
= SecurityPermissionFlag
.SerializationFormatter
)]
503 public static object Deserialize(Stream stream
) {
504 BinaryFormatter formatter
= new BinaryFormatter();
505 Object data
= formatter
.Deserialize(stream
);
506 if (!(data
is OutputCacheEntry
|| data
is PartialCachingCacheEntry
|| data
is CachedVary
|| data
is ControlCachedVary
||
507 data
is FileResponseElement
|| data
is MemoryResponseElement
|| data
is SubstitutionResponseElement
)) {
508 throw new ArgumentException(SR
.GetString(SR
.OutputCacheExtensibility_CantSerializeDeserializeType
));
513 // lookup cached vary
515 // lookup entry for content-encoding
516 internal static Object
Get(String key
) {
517 // if it's not in the provider or the default provider is undefined,
518 // check the internal cache (we don't know where it is).
519 Object result
= null;
520 OutputCacheProvider provider
= GetProvider(HttpContext
.Current
);
521 if (provider
!= null) {
522 result
= provider
.Get(key
);
523 OutputCacheEntry oce
= result
as OutputCacheEntry
;
525 if (HasDependencyChanged(false /*isFragment*/, oce
.DependenciesKey
, oce
.Dependencies
, oce
.KernelCacheUrl
, key
, provider
.Name
)) {
526 OutputCache
.RemoveFromProvider(key
, provider
.Name
);
528 Debug
.Trace("OutputCache", "Get(" + key
+ ") --> null, " + provider
.Name
);
533 result
= Convert(oce
);
536 if (result
== null) {
537 result
= HttpRuntime
.Cache
.InternalCache
.Get(key
);
539 string typeName
= (result
!= null) ? result
.GetType().Name
: "null";
540 Debug
.Trace("OutputCache", "Get(" + key
+ ") --> " + typeName
+ ", CacheInternal");
543 Debug
.Trace("OutputCache", "Get(" + key
+ ") --> " + result
.GetType().Name
+ ", " + provider
.Name
);
550 internal static Object
GetFragment(String key
, String providerName
) {
551 // if providerName is null, use default provider.
552 // if providerName is not null, get it from the provider collection.
553 // if it's not in the provider or the default provider is undefined,
554 // check the internal cache (we don't know where it is).
555 Object result
= null;
556 OutputCacheProvider provider
= GetFragmentProvider(providerName
);
557 if (provider
!= null) {
558 result
= provider
.Get(key
);
559 PartialCachingCacheEntry fragment
= result
as PartialCachingCacheEntry
;
560 if (fragment
!= null) {
561 if (HasDependencyChanged(true /*isFragment*/, fragment
._dependenciesKey
, fragment
._dependencies
, null /*kernelKey*/, key
, provider
.Name
)) {
562 OutputCache
.RemoveFragment(key
, provider
.Name
);
564 Debug
.Trace("OutputCache", "GetFragment(" + key
+ "," + providerName
+ ") --> null, " + providerName
);
571 if (result
== null) {
572 result
= HttpRuntime
.Cache
.InternalCache
.Get(key
);
574 string typeName
= (result
!= null) ? result
.GetType().Name
: "null";
575 Debug
.Trace("OutputCache", "GetFragment(" + key
+ "," + providerName
+ ") --> " + typeName
+ ", CacheInternal");
578 Debug
.Trace("OutputCache", "GetFragment(" + key
+ "," + providerName
+ ") --> " + result
.GetType().Name
+ ", " + providerName
);
586 internal static void Remove(String key
, HttpContext context
) {
587 // we don't know if it's in the internal cache or
588 // one of the providers. If a context is given,
589 // then we can narrow down to at most one provider.
590 // If the context is null, then we don't know which
591 // provider and we have to check all.
593 HttpRuntime
.Cache
.InternalCache
.Remove(key
);
595 if (context
== null) {
596 // remove from all providers since we don't know which one it's in.
597 OutputCacheProviderCollection providers
= Providers
;
598 if (providers
!= null) {
599 foreach (OutputCacheProvider provider
in providers
) {
600 provider
.Remove(key
);
605 OutputCacheProvider provider
= GetProvider(context
);
606 if (provider
!= null) {
607 provider
.Remove(key
);
611 Debug
.Trace("OutputCache", "Remove(" + key
+ ", context)");
617 internal static void RemoveFromProvider(String key
, String providerName
) {
618 // we know where it is. If providerName is given,
619 // then it is in that provider. If it's not given,
620 // it's in the internal cache.
621 if (providerName
== null) {
622 throw new ArgumentNullException("providerName");
625 OutputCacheProviderCollection providers
= Providers
;
626 OutputCacheProvider provider
= (providers
== null) ? null : providers
[providerName
];
627 if (provider
== null) {
628 throw new ProviderException(SR
.GetString(SR
.Provider_Not_Found
, providerName
));
631 provider
.Remove(key
);
633 Debug
.Trace("OutputCache", "Remove(" + key
+ ", " + providerName
+ ")");
638 internal static void RemoveFragment(String key
, String providerName
) {
639 // if providerName is null, use default provider.
640 // if providerName is not null, get it from the provider collection.
641 // remove it from the provider and the internal cache (we don't know where it is).
642 OutputCacheProvider provider
= GetFragmentProvider(providerName
);
643 if (provider
!= null) {
644 provider
.Remove(key
);
646 HttpRuntime
.Cache
.InternalCache
.Remove(key
);
648 Debug
.Trace("OutputCache", "RemoveFragment(" + key
+ "," + providerName
+ ")");
653 internal static void InsertFragment(String cachedVaryKey
, ControlCachedVary cachedVary
,
654 String fragmentKey
, PartialCachingCacheEntry fragment
,
655 CacheDependency dependencies
,
656 DateTime absExp
, TimeSpan slidingExp
,
657 String providerName
) {
659 // if providerName is not null, find the provider in the collection.
660 // if providerName is null, use default provider.
661 // if the default provider is undefined or the fragment can't be inserted in the
662 // provider, insert it in the internal cache.
663 OutputCacheProvider provider
= GetFragmentProvider(providerName
);
666 // ControlCachedVary and PartialCachingCacheEntry can be serialized
669 bool useProvider
= (provider
!= null);
671 bool canUseProvider
= (slidingExp
== Cache
.NoSlidingExpiration
672 && (dependencies
== null || dependencies
.IsFileDependency()));
674 if (useProvider
&& !canUseProvider
) {
675 throw new ProviderException(SR
.GetString(SR
.Provider_does_not_support_policy_for_fragments
, providerName
));
680 bool cachedVaryPutInCache
= (cachedVary
!= null);
682 if (cachedVary
!= null) {
683 // Add the ControlCachedVary item so that a request will know
684 // which varies are needed to issue another request.
686 // Use the Add method so that we guarantee we only use
687 // a single ControlCachedVary and don't overwrite existing ones.
688 ControlCachedVary cachedVaryInCache
;
690 cachedVaryInCache
= OutputCache
.UtcAdd(cachedVaryKey
, cachedVary
);
693 cachedVaryInCache
= (ControlCachedVary
) provider
.Add(cachedVaryKey
, cachedVary
, Cache
.NoAbsoluteExpiration
);
696 if (cachedVaryInCache
!= null) {
697 if (!cachedVary
.Equals(cachedVaryInCache
)) {
698 // overwrite existing cached vary
700 HttpRuntime
.Cache
.InternalCache
.Insert(cachedVaryKey
, cachedVary
, null);
703 provider
.Set(cachedVaryKey
, cachedVary
, Cache
.NoAbsoluteExpiration
);
707 cachedVary
= cachedVaryInCache
;
709 cachedVaryPutInCache
= false;
715 AddCacheKeyToDependencies(ref dependencies
, cachedVaryKey
);
718 // not all caches support cache key dependencies, but we can use a "change number" to associate
719 // the ControlCachedVary and the PartialCachingCacheEntry
720 fragment
._cachedVaryId
= cachedVary
.CachedVaryId
;
723 // Now insert into the cache (use cache provider if possible, otherwise use internal cache)
725 HttpRuntime
.Cache
.InternalCache
.Insert(fragmentKey
, fragment
, new CacheInsertOptions() {
726 Dependencies
= dependencies
,
727 AbsoluteExpiration
= absExp
,
728 SlidingExpiration
= slidingExp
732 string depKey
= null;
733 if (dependencies
!= null) {
734 depKey
= OUTPUTCACHE_KEYPREFIX_DEPENDENCIES
+ dependencies
.GetUniqueID();
735 fragment
._dependenciesKey
= depKey
;
736 fragment
._dependencies
= dependencies
.GetFileDependencies();
738 provider
.Set(fragmentKey
, fragment
, absExp
);
739 if (dependencies
!= null) {
740 // use Add and dispose dependencies if there's already one in the cache
741 Object d
= HttpRuntime
.Cache
.InternalCache
.Add(depKey
, new DependencyCacheEntry(fragmentKey
, null, provider
.Name
),
742 new CacheInsertOptions() {
743 Dependencies
= dependencies
,
744 AbsoluteExpiration
= absExp
,
745 OnRemovedCallback
= s_dependencyRemovedCallbackForFragment
748 dependencies
.Dispose();
754 string cachedVaryType
= (cachedVaryPutInCache
) ? "ControlCachedVary" : "";
755 string providerUsed
= (useProvider
) ? provider
.Name
: "CacheInternal";
756 Debug
.Trace("OutputCache", "InsertFragment("
757 + cachedVaryKey
+ ", "
758 + cachedVaryType
+ ", "
759 + fragmentKey
+ ", PartialCachingCacheEntry, ...) -->"
764 // insert cached vary or output cache entry
765 internal static void InsertResponse(String cachedVaryKey
, CachedVary cachedVary
,
766 String rawResponseKey
, CachedRawResponse rawResponse
,
767 CacheDependency dependencies
,
768 DateTime absExp
, TimeSpan slidingExp
) {
770 // if the provider is undefined or the fragment can't be inserted in the
771 // provider, insert it in the internal cache.
772 OutputCacheProvider provider
= GetProvider(HttpContext
.Current
);
775 // CachedVary can be serialized.
776 // CachedRawResponse is not always serializable.
779 bool useProvider
= (provider
!= null);
781 bool canUseProvider
= (IsSubstBlockSerializable(rawResponse
._rawResponse
)
782 && rawResponse
._settings
.IsValidationCallbackSerializable()
783 && slidingExp
== Cache
.NoSlidingExpiration
784 && (dependencies
== null || dependencies
.IsFileDependency()));
786 if (useProvider
&& !canUseProvider
) {
787 throw new ProviderException(SR
.GetString(SR
.Provider_does_not_support_policy_for_responses
, provider
.Name
));
792 bool cachedVaryPutInCache
= (cachedVary
!= null);
794 if (cachedVary
!= null) {
796 * Add the CachedVary item so that a request will know
797 * which headers are needed to issue another request.
799 * Use the Add method so that we guarantee we only use
800 * a single CachedVary and don't overwrite existing ones.
803 CachedVary cachedVaryInCache
;
805 cachedVaryInCache
= OutputCache
.UtcAdd(cachedVaryKey
, cachedVary
);
808 cachedVaryInCache
= (CachedVary
) provider
.Add(cachedVaryKey
, cachedVary
, Cache
.NoAbsoluteExpiration
);
811 if (cachedVaryInCache
!= null) {
812 if (!cachedVary
.Equals(cachedVaryInCache
)) {
814 HttpRuntime
.Cache
.InternalCache
.Insert(cachedVaryKey
, cachedVary
, null);
817 provider
.Set(cachedVaryKey
, cachedVary
, Cache
.NoAbsoluteExpiration
);
821 cachedVary
= cachedVaryInCache
;
823 cachedVaryPutInCache
= false;
829 AddCacheKeyToDependencies(ref dependencies
, cachedVaryKey
);
832 // not all caches support cache key dependencies, but we can use a "change number" to associate
833 // the ControlCachedVary and the PartialCachingCacheEntry
834 rawResponse
._cachedVaryId
= cachedVary
.CachedVaryId
;
837 // Now insert into the cache (use cache provider if possible, otherwise use internal cache)
839 HttpRuntime
.Cache
.InternalCache
.Insert(rawResponseKey
, rawResponse
, new CacheInsertOptions() {
840 Dependencies
= dependencies
,
841 AbsoluteExpiration
= absExp
,
842 SlidingExpiration
= slidingExp
,
843 OnRemovedCallback
= s_entryRemovedCallback
848 PerfCounters
.IncrementCounter(AppPerfCounter
.OUTPUT_CACHE_ENTRIES
);
849 PerfCounters
.IncrementCounter(AppPerfCounter
.OUTPUT_CACHE_TURNOVER_RATE
);
852 string depKey
= null;
853 string[] fileDeps
= null;
854 if (dependencies
!= null) {
855 depKey
= OUTPUTCACHE_KEYPREFIX_DEPENDENCIES
+ dependencies
.GetUniqueID();
856 fileDeps
= dependencies
.GetFileDependencies();
858 OutputCacheEntry oce
= Convert(rawResponse
, depKey
, fileDeps
);
859 provider
.Set(rawResponseKey
, oce
, absExp
);
860 if (dependencies
!= null) {
861 // use Add and dispose dependencies if there's already one in the cache
862 Object d
= HttpRuntime
.Cache
.InternalCache
.Add(depKey
, new DependencyCacheEntry(rawResponseKey
, oce
.KernelCacheUrl
, provider
.Name
),
863 new CacheInsertOptions() {
864 Dependencies
= dependencies
,
865 AbsoluteExpiration
= absExp
,
866 OnRemovedCallback
= s_dependencyRemovedCallbackForFragment
869 dependencies
.Dispose();
874 string cachedVaryType
= (cachedVaryPutInCache
) ? "CachedVary" : "";
875 string providerUsed
= (useProvider
) ? provider
.Name
: "CacheInternal";
876 Debug
.Trace("OutputCache", "InsertResposne("
877 + cachedVaryKey
+ ", "
878 + cachedVaryType
+ ", "
879 + rawResponseKey
+ ", CachedRawResponse, ...) -->"