Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Web / UI / PartialCachingControl.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="PartialCachingControl.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.UI {
8
9 using System;
10 using System.IO;
11 using System.Text;
12 using System.Collections;
13 using System.Collections.Specialized;
14 using System.ComponentModel;
15 using System.ComponentModel.Design;
16 using System.Globalization;
17 using System.Web;
18 using System.Web.Util;
19 using System.Web.UI.HtmlControls;
20 using System.Web.UI.WebControls;
21 using System.Web.Caching;
22 using System.Web.Compilation;
23 using System.Web.Configuration;
24 using System.Security.Permissions;
25
26
27 // Keeps track of one call to Page Register* API
28 // The semantics of the fields depends to the call type
29 [Serializable]
30 internal class RegisterCallData {
31     internal ClientAPIRegisterType Type;
32     internal ScriptKey Key;
33     internal string StringParam1;
34     internal string StringParam2;
35     internal string StringParam3;
36 }
37
38 // Data that we need to cache
39 [Serializable]
40 internal class PartialCachingCacheEntry {
41     internal    Guid                      _cachedVaryId;
42     internal    string                    _dependenciesKey;
43     internal    string[]                  _dependencies; // file dependencies
44
45     internal string OutputString;
46     internal string CssStyleString;
47     internal ArrayList RegisteredClientCalls;
48 }
49
50 /// <devdoc>
51 ///    <para>[To be supplied.]</para>
52 /// </devdoc>
53 [
54 ToolboxItem(false)
55 ]
56 public abstract class BasePartialCachingControl : Control {
57
58     internal Control _cachedCtrl;
59     private long _nonVaryHashCode;
60     internal string _ctrlID;
61     internal string _guid;
62     internal DateTime _utcExpirationTime;
63     internal bool _useSlidingExpiration;
64     internal HttpCacheVaryByParams _varyByParamsCollection;
65     internal string[] _varyByControlsCollection;
66     internal string _varyByCustom;
67     internal string _sqlDependency;
68     internal string _provider;
69     internal bool _cachingDisabled;
70     private string _outputString;
71     private string _cssStyleString;
72     private string _cacheKey;
73     private CacheDependency _cacheDependency;
74     private PartialCachingCacheEntry _cacheEntry;
75     private ControlCachePolicy _cachePolicy;
76     private ArrayList _registeredCallDataForEventValidation;
77     private ArrayList _registeredStyleInfo = null;
78
79     internal const char varySeparator = ';';
80     internal const string varySeparatorString = ";";
81
82     internal override void InitRecursive(Control namingContainer) {
83
84         HashCodeCombiner combinedHashCode = new HashCodeCombiner();
85
86         _cacheKey = ComputeNonVaryCacheKey(combinedHashCode);
87
88         // Save the non-varying hash, so we don't need to recalculate it later
89         _nonVaryHashCode = combinedHashCode.CombinedHash;
90
91         PartialCachingCacheEntry cacheEntry = null;
92
93         // Check if there is a cache entry for the non-varying key
94         object tmpCacheEntry = OutputCache.GetFragment(_cacheKey, _provider);
95
96         if (tmpCacheEntry != null) {
97             ControlCachedVary cachedVary = tmpCacheEntry as ControlCachedVary;
98             if (cachedVary != null) {
99                 string varyCachedKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
100
101                 // Check if there is a cache entry for the varying key
102                 cacheEntry = (PartialCachingCacheEntry) OutputCache.GetFragment(varyCachedKey, _provider);
103                 if (cacheEntry != null && cacheEntry._cachedVaryId != cachedVary.CachedVaryId) {
104                     cacheEntry = null;
105                     // explicitly remove the entry
106                     OutputCache.RemoveFragment(varyCachedKey, _provider);
107                 }
108             }
109             else {
110                 // If it wasn't a ControlCachedVary, it must be a PartialCachingCacheEntry
111                 cacheEntry = (PartialCachingCacheEntry) tmpCacheEntry;
112             }
113         }
114
115         // If it's a cache miss, create the control and make it our child
116         if (cacheEntry == null) {
117
118             // Cache miss
119
120             _cacheEntry = new PartialCachingCacheEntry();
121
122             _cachedCtrl = CreateCachedControl();
123             Controls.Add(_cachedCtrl);
124
125             // Make sure the Page knows about us while the control's OnInit is called
126             Page.PushCachingControl(this);
127             base.InitRecursive(namingContainer);
128             Page.PopCachingControl();
129         }
130         else {
131
132             // Cache hit
133
134             _outputString = cacheEntry.OutputString;
135             _cssStyleString = cacheEntry.CssStyleString;
136
137             // If any calls to Register* API's were made when the control was run,
138             // make them now to restore correct behavior (VSWhidbey 80907)
139             if (cacheEntry.RegisteredClientCalls != null) {
140                 foreach (RegisterCallData registerCallData in cacheEntry.RegisteredClientCalls) {
141                     switch (registerCallData.Type) {
142
143                         case ClientAPIRegisterType.WebFormsScript:
144                             Page.RegisterWebFormsScript();
145                             break;
146
147                         case ClientAPIRegisterType.PostBackScript:
148                             Page.RegisterPostBackScript();
149                             break;
150
151                         case ClientAPIRegisterType.FocusScript:
152                             Page.RegisterFocusScript();
153                             break;
154
155                         case ClientAPIRegisterType.ClientScriptBlocks:
156                         case ClientAPIRegisterType.ClientScriptBlocksWithoutTags:
157                         case ClientAPIRegisterType.ClientStartupScripts:
158                         case ClientAPIRegisterType.ClientStartupScriptsWithoutTags:
159                             Page.ClientScript.RegisterScriptBlock(registerCallData.Key,
160                                 registerCallData.StringParam2, registerCallData.Type);
161                             break;
162
163                         case ClientAPIRegisterType.OnSubmitStatement:
164                             Page.ClientScript.RegisterOnSubmitStatementInternal(registerCallData.Key,
165                                 registerCallData.StringParam2);
166                             break;
167
168                         case ClientAPIRegisterType.ArrayDeclaration:
169                             Page.ClientScript.RegisterArrayDeclaration(registerCallData.StringParam1,
170                                 registerCallData.StringParam2);
171                             break;
172
173                         case ClientAPIRegisterType.HiddenField:
174                             Page.ClientScript.RegisterHiddenField(registerCallData.StringParam1,
175                                 registerCallData.StringParam2);
176                             break;
177
178                         case ClientAPIRegisterType.ExpandoAttribute:
179                             Page.ClientScript.RegisterExpandoAttribute(registerCallData.StringParam1,
180                                 registerCallData.StringParam2, registerCallData.StringParam3, false);
181                             break;
182
183                         case ClientAPIRegisterType.EventValidation:
184                             if (_registeredCallDataForEventValidation == null) {
185                                 _registeredCallDataForEventValidation = new ArrayList();
186                             }
187
188                             _registeredCallDataForEventValidation.Add(registerCallData);
189                             break;
190                         default:
191                             Debug.Assert(false);
192                             break;
193                     }
194                 }
195             }
196
197             base.InitRecursive(namingContainer);
198         }
199     }
200
201     internal override void LoadRecursive() {
202
203         // If we're in a cache hit, don't do anything special
204         if (_outputString != null) {
205             base.LoadRecursive();
206             return;
207         }
208
209         // Make sure the Page knows about us while the control's OnLoad is called
210         Page.PushCachingControl(this);
211         base.LoadRecursive();
212         Page.PopCachingControl();
213     }
214
215     internal override void PreRenderRecursiveInternal() {
216
217         // If we're in a cache hit, don't do anything special
218         if (_outputString != null) {
219             base.PreRenderRecursiveInternal();
220
221             // register the cached styles on the Header control.
222             if (_cssStyleString != null && Page.Header != null) {
223                 Page.Header.RegisterCssStyleString(_cssStyleString);
224             }
225
226             return;
227         }
228
229         // Make sure the Page knows about us while the control's OnPreRender is called
230         Page.PushCachingControl(this);
231         base.PreRenderRecursiveInternal();
232         Page.PopCachingControl();
233     }
234
235
236     /// <internalonly/>
237     public override void Dispose() {
238         if (_cacheDependency != null) {
239             _cacheDependency.Dispose();
240             _cacheDependency = null;
241         }
242
243         base.Dispose();
244     }
245
246     internal abstract Control CreateCachedControl();
247
248
249     /// <devdoc>
250     ///    <para> Gets or sets the CacheDependency used to cache the control output.</para>
251     /// </devdoc>
252     public CacheDependency Dependency {
253         get { return _cacheDependency; }
254         set { _cacheDependency = value; }
255     }
256
257
258     public ControlCachePolicy CachePolicy {
259         get {
260             // Create the ControlCachePolicy object on demand
261             if (_cachePolicy == null)
262                 _cachePolicy = new ControlCachePolicy(this);
263
264             return _cachePolicy;
265         }
266     }
267
268     internal HttpCacheVaryByParams VaryByParams {
269         get {
270             if (_varyByParamsCollection == null) {
271                 _varyByParamsCollection = new HttpCacheVaryByParams();
272                 _varyByParamsCollection.IgnoreParams = true;
273             }
274
275             return _varyByParamsCollection;
276         }
277     }
278     
279     internal string VaryByControl {
280         get {
281             if (_varyByControlsCollection == null)
282                 return String.Empty;
283
284             return String.Join(varySeparatorString, _varyByControlsCollection);
285         }
286         
287         set {
288             if (String.IsNullOrEmpty(value)) {
289                 _varyByControlsCollection = null;
290             }
291             else {
292                 _varyByControlsCollection = value.Split(varySeparator);
293             }
294         }
295     }
296     
297     internal TimeSpan Duration {
298         get {
299             // Special case MaxValue
300             if (_utcExpirationTime == DateTime.MaxValue)
301                 return TimeSpan.MaxValue;
302
303             return _utcExpirationTime - DateTime.UtcNow;
304         }
305         
306         set {
307             if (value == TimeSpan.MaxValue) {
308                 // If it's the max timespan, just make it DateTime.MaxValue to avoid
309                 // an overflow when adding (VSWhidbey 273271)
310                 _utcExpirationTime = DateTime.MaxValue;
311             }
312             else {
313                 // Compute the expiration time
314                 _utcExpirationTime = DateTime.UtcNow.Add(value);
315             }
316         }
317     }
318
319     private void RegisterValidationEvents() {
320         if (_registeredCallDataForEventValidation != null) {
321             foreach (RegisterCallData registerCallData in _registeredCallDataForEventValidation) {
322                 Page.ClientScript.RegisterForEventValidation(registerCallData.StringParam1,
323                     registerCallData.StringParam2);
324             }
325         }
326     }
327
328     internal void RegisterStyleInfo(SelectorStyleInfo selectorInfo) {
329         if (_registeredStyleInfo == null) {
330             _registeredStyleInfo = new ArrayList();
331         }
332
333         _registeredStyleInfo.Add(selectorInfo);
334     }
335
336     /// <internalonly/>
337     /// <devdoc>
338     ///    <para>[To be supplied.]</para>
339     /// </devdoc>
340     protected internal override void Render(HtmlTextWriter output) {
341         CacheDependency sqlCacheDep = null;
342
343         // If the output is cached, use it and do nothing else
344         if (_outputString != null) {
345             output.Write(_outputString);
346             RegisterValidationEvents();
347             return;
348         }
349
350         // If caching was turned off, just render the control
351         if (_cachingDisabled || !RuntimeConfig.GetAppConfig().OutputCache.EnableFragmentCache) {
352             _cachedCtrl.RenderControl(output);
353             return;
354         }
355
356         // Create SQL cache dependency before we render the page
357         if (_sqlDependency != null) {
358             sqlCacheDep = SqlCacheDependency.CreateOutputCacheDependency(_sqlDependency);
359         }
360
361         _cacheEntry.CssStyleString = GetCssStyleRenderString(output.GetType());
362
363         // Create a new HtmlTextWriter, with the same type as the current one (see ASURT 118922)
364         StringWriter tmpWriter = new StringWriter();
365         HtmlTextWriter tmpHtmlWriter = Page.CreateHtmlTextWriterFromType(tmpWriter, output.GetType());
366         CacheDependency cacheDep;
367         TextWriter savedWriter = Context.Response.SwitchWriter(tmpWriter);
368
369         try {
370             // Make sure the Page knows about us while the control's OnPreRender is called
371             Page.PushCachingControl(this);
372             _cachedCtrl.RenderControl(tmpHtmlWriter);
373             Page.PopCachingControl();
374         }
375         finally {
376             Context.Response.SwitchWriter(savedWriter);
377         }
378
379         _cacheEntry.OutputString = tmpWriter.ToString();
380
381         // Send the output to the response
382         output.Write(_cacheEntry.OutputString);
383
384         // Cache the output
385
386         cacheDep = _cacheDependency;
387
388         if (sqlCacheDep != null) {
389             if (cacheDep == null) {
390                 cacheDep = sqlCacheDep;
391             }
392             else {
393                 AggregateCacheDependency aggr = new AggregateCacheDependency();
394
395                 aggr.Add(cacheDep);
396                 aggr.Add(sqlCacheDep);
397                 cacheDep = aggr;
398             }
399         }
400
401         ControlCachedVary cachedVary = null;
402         string realItemCacheKey;
403         // If there are no varies, use the non-varying key
404         if (_varyByParamsCollection == null && _varyByControlsCollection == null && _varyByCustom == null) {
405             realItemCacheKey = _cacheKey;
406         }
407         else {
408             string[] varyByParams = null;
409             if (_varyByParamsCollection != null)
410                 varyByParams = _varyByParamsCollection.GetParams();
411
412             cachedVary = new ControlCachedVary(varyByParams, _varyByControlsCollection, _varyByCustom);
413
414             HashCodeCombiner combinedHashCode = new HashCodeCombiner(_nonVaryHashCode);
415             realItemCacheKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
416         }
417
418         // Compute the correct expiration, sliding or absolute
419         DateTime utcExpirationTime;
420         TimeSpan slidingExpiration;
421         if (_useSlidingExpiration) {
422             utcExpirationTime = Cache.NoAbsoluteExpiration;
423             slidingExpiration = _utcExpirationTime - DateTime.UtcNow;
424         }
425         else {
426             utcExpirationTime = _utcExpirationTime;
427             slidingExpiration = Cache.NoSlidingExpiration;
428         }
429         
430         try {
431             OutputCache.InsertFragment(_cacheKey, cachedVary,
432                                        realItemCacheKey, _cacheEntry,
433                                        cacheDep /*dependencies*/,
434                                        utcExpirationTime, slidingExpiration,
435                                        _provider);
436         }
437         catch {
438             if (cacheDep != null) {
439                 cacheDep.Dispose();
440             }
441             throw;
442         }
443     }
444
445     // Return the key used to cache the output
446     private string ComputeNonVaryCacheKey(HashCodeCombiner combinedHashCode) {
447         // Create a cache key by combining various elements
448
449         // Start with the guid
450         combinedHashCode.AddObject(_guid);
451
452         // Make the key vary based on the type of the writer (ASURT 118922)
453         HttpBrowserCapabilities browserCap = Context.Request.Browser;
454         if (browserCap != null)
455             combinedHashCode.AddObject(browserCap.TagWriter);
456
457         return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
458     }
459
460     private string ComputeVaryCacheKey(HashCodeCombiner combinedHashCode,
461         ControlCachedVary cachedVary) {
462
463         // Add something to the has to differentiate it from the non-vary hash.
464         // This is needed in case this method doesn't add anything else to the hash (VSWhidbey 194199)
465         combinedHashCode.AddInt(1);
466
467         // Get the request value collection
468         NameValueCollection reqValCollection;
469         HttpRequest request = Page.Request;
470         if (request != null && request.HttpVerb == HttpVerb.POST) {
471             // Bug 6129: Partial cache key should include posted form values in postbacks.
472             // Include both QueryString and Form values (but not Cookies or Server Variables like Request.Params does).
473             // Per Request.Params behavior, add QueryString values before Form values
474             reqValCollection = new NameValueCollection(request.QueryString);
475             reqValCollection.Add(request.Form);
476         }
477         else {
478             // Use the existing value if possible to avoid recreating a NameValueCollection
479             reqValCollection = Page.RequestValueCollection;
480             // If it's not set, get it based on the method
481             if (reqValCollection == null) {
482                 reqValCollection = Page.GetCollectionBasedOnMethod(true /*dontReturnNull*/);
483             }
484         }
485
486         if (cachedVary._varyByParams != null) {
487
488             ICollection itemsToUseForHashCode;
489
490             // If '*' was specified, use all the items in the request collection.
491             // Otherwise, use only those specified.
492             if (cachedVary._varyByParams.Length == 1 && cachedVary._varyByParams[0] == "*")
493                 itemsToUseForHashCode = reqValCollection;
494             else
495                 itemsToUseForHashCode = cachedVary._varyByParams;
496
497             // Add the items and their values to compute the hash code
498             foreach (string varyByParam in itemsToUseForHashCode) {
499
500                 // Note: we use to ignore certain system fields here (like VIEWSTATE), but decided
501                 // not to for consistency with pahe output caching (VSWhidbey 196267, 479252)
502
503                 combinedHashCode.AddCaseInsensitiveString(varyByParam);
504                 string val = reqValCollection[varyByParam];
505                 if (val != null)
506                     combinedHashCode.AddObject(val);
507             }
508         }
509
510         if (cachedVary._varyByControls != null) {
511
512             // Prepend them with a prefix to make them fully qualified
513             string prefix;
514             if (NamingContainer == Page) {
515                 // No prefix if it's the page
516                 prefix = String.Empty;
517             }
518             else {
519                 prefix = NamingContainer.UniqueID;
520                 Debug.Assert(!String.IsNullOrEmpty(prefix));
521                 prefix += IdSeparator;
522             }
523
524             prefix += _ctrlID + IdSeparator;
525
526             // Add all the relative vary params and their values to the hash code
527             foreach (string varyByParam in cachedVary._varyByControls) {
528
529                 string temp = prefix + varyByParam.Trim();
530                 combinedHashCode.AddCaseInsensitiveString(temp);
531                 string val = reqValCollection[temp];
532                 if (val != null)
533                     combinedHashCode.AddObject(reqValCollection[temp]);
534             }
535         }
536
537         if (cachedVary._varyByCustom != null) {
538             string customString = Context.ApplicationInstance.GetVaryByCustomString(
539                 Context, cachedVary._varyByCustom);
540             if (customString != null)
541                 combinedHashCode.AddObject(customString);
542         }
543
544         return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
545     }
546
547     private string GetCssStyleRenderString(Type htmlTextWriterType) {
548         // Nothing to do if no styles are registered.
549         if (_registeredStyleInfo == null) {
550             return null;
551         }
552
553         // Create an empty cssStringWriter
554         StringWriter cssStringWriter = new StringWriter(CultureInfo.CurrentCulture);
555
556         // Create a new HtmlTextWriter, with the same type as the current one
557         HtmlTextWriter cssHtmlTextWriter = 
558             Page.CreateHtmlTextWriterFromType(cssStringWriter, htmlTextWriterType);
559
560         CssTextWriter cssWriter = new CssTextWriter(cssHtmlTextWriter);
561
562         foreach (SelectorStyleInfo si in _registeredStyleInfo) {
563             HtmlHead.RenderCssRule(cssWriter, si.selector, si.style, si.urlResolver);
564         }
565
566         // Return the css style rendered string
567         return cssStringWriter.ToString();
568     }
569
570     internal void SetVaryByParamsCollectionFromString(string varyByParams) {
571
572         Debug.Assert(_varyByParamsCollection == null);
573
574         if (varyByParams == null)
575             return;
576
577         string[] varyByParamsStrings = varyByParams.Split(varySeparator);
578         _varyByParamsCollection = new HttpCacheVaryByParams();
579         _varyByParamsCollection.ResetFromParams(varyByParamsStrings);
580     }
581
582     internal void RegisterPostBackScript() {
583         RegisterClientCall(ClientAPIRegisterType.PostBackScript, String.Empty, null);
584     }
585
586     internal void RegisterFocusScript() {
587         RegisterClientCall(ClientAPIRegisterType.FocusScript, String.Empty, null);
588     }
589
590     internal void RegisterWebFormsScript() {
591         RegisterClientCall(ClientAPIRegisterType.WebFormsScript, String.Empty, null);
592     }
593
594     private void RegisterClientCall(ClientAPIRegisterType type,
595         ScriptKey scriptKey, string stringParam2) {
596
597         // Keep track of the call, in order to be able to call it again when there is a cache hit.
598
599         RegisterCallData registerCallData = new RegisterCallData();
600         registerCallData.Type = type;
601         registerCallData.Key = scriptKey;
602         registerCallData.StringParam2 = stringParam2;
603
604         if (_cacheEntry.RegisteredClientCalls == null)
605             _cacheEntry.RegisteredClientCalls = new ArrayList();
606
607         _cacheEntry.RegisteredClientCalls.Add(registerCallData);
608     }
609
610     private void RegisterClientCall(ClientAPIRegisterType type,
611         string stringParam1, string stringParam2) {
612         RegisterClientCall(type, stringParam1, stringParam2, null);
613     }
614
615     private void RegisterClientCall(ClientAPIRegisterType type,
616         string stringParam1, string stringParam2, string stringParam3) {
617
618         // Keep track of the call, in order to be able to call it again when there is a cache hit.
619
620         RegisterCallData registerCallData = new RegisterCallData();
621         registerCallData.Type = type;
622         registerCallData.StringParam1 = stringParam1;
623         registerCallData.StringParam2 = stringParam2;
624         registerCallData.StringParam3 = stringParam3;
625
626         if (_cacheEntry.RegisteredClientCalls == null)
627             _cacheEntry.RegisteredClientCalls = new ArrayList();
628
629         _cacheEntry.RegisteredClientCalls.Add(registerCallData);
630     }
631
632     internal void RegisterScriptBlock(ClientAPIRegisterType type, ScriptKey key, string script) {
633         RegisterClientCall(type, key, script);
634     }
635
636     internal void RegisterOnSubmitStatement(ScriptKey key, string script) {
637         RegisterClientCall(ClientAPIRegisterType.OnSubmitStatement, key, script);
638     }
639
640     internal void RegisterArrayDeclaration(string arrayName, string arrayValue) {
641         RegisterClientCall(ClientAPIRegisterType.ArrayDeclaration,
642             arrayName, arrayValue);
643     }
644
645     internal void RegisterHiddenField(string hiddenFieldName, string hiddenFieldInitialValue) {
646         RegisterClientCall(ClientAPIRegisterType.HiddenField,
647             hiddenFieldName, hiddenFieldInitialValue);
648     }
649
650     internal void RegisterExpandoAttribute(string controlID, string attributeName, string attributeValue) {
651         RegisterClientCall(ClientAPIRegisterType.ExpandoAttribute, controlID, attributeName, attributeValue);
652     }
653
654     internal void RegisterForEventValidation(string uniqueID, string argument) {
655         RegisterClientCall(ClientAPIRegisterType.EventValidation, uniqueID, argument);
656     }
657 }
658
659
660 /// <devdoc>
661 ///    <para>[To be supplied.]</para>
662 /// </devdoc>
663 public class StaticPartialCachingControl : BasePartialCachingControl {
664
665     private BuildMethod _buildMethod;
666
667
668     /// <internalonly/>
669     /// <devdoc>
670     ///    <para>[To be supplied.]</para>
671     /// </devdoc>
672     public StaticPartialCachingControl(string ctrlID, string guid, int duration,
673         string varyByParams, string varyByControls, string varyByCustom,
674         BuildMethod buildMethod)
675         :this(ctrlID, guid, duration, varyByParams, varyByControls,
676             varyByCustom, null, buildMethod, null)
677     {
678     }
679
680
681     /// <internalonly/>
682     /// <devdoc>
683     ///    <para>[To be supplied.]</para>
684     /// </devdoc>
685     public StaticPartialCachingControl(string ctrlID, string guid, int duration,
686         string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
687         BuildMethod buildMethod)
688         :this(ctrlID, guid, duration, varyByParams, varyByControls,
689             varyByCustom, sqlDependency, buildMethod, null)
690     {
691     }
692
693     public StaticPartialCachingControl(string ctrlID, string guid, int duration,
694         string varyByParams, string varyByControls, string varyByCustom, string sqlDependency, 
695         BuildMethod buildMethod, string providerName) {
696         _ctrlID = ctrlID;
697         Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, duration /*seconds*/);
698
699         SetVaryByParamsCollectionFromString(varyByParams);
700
701         if (varyByControls != null)
702             _varyByControlsCollection = varyByControls.Split(varySeparator);
703         _varyByCustom = varyByCustom;
704         _guid = guid;
705         _buildMethod = buildMethod;
706         _sqlDependency = sqlDependency;
707         _provider = providerName;
708     }
709
710     internal override Control CreateCachedControl() {
711         return _buildMethod();
712     }
713
714     /*
715      * Called by generated code (hence must be public).
716      * Create a StaticPartialCachingControl and add it as a child
717      */
718
719     /// <internalonly/>
720     /// <devdoc>
721     ///    <para>[To be supplied.]</para>
722     /// </devdoc>
723     static public void BuildCachedControl(Control parent, string ctrlID, string guid,
724         int duration, string varyByParams, string varyByControls, string varyByCustom,
725         BuildMethod buildMethod) {
726         BuildCachedControl(parent, ctrlID, guid, duration, varyByParams,
727             varyByControls, varyByCustom, null, buildMethod, null);
728     }
729
730
731     /// <internalonly/>
732     /// <devdoc>
733     ///    <para>[To be supplied.]</para>
734     /// </devdoc>
735     static public void BuildCachedControl(Control parent, string ctrlID, string guid,
736         int duration, string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
737         BuildMethod buildMethod) {
738         BuildCachedControl(parent, ctrlID, guid, duration, varyByParams,
739             varyByControls, varyByCustom, sqlDependency, buildMethod, null);
740     }
741
742     static public void BuildCachedControl(Control parent, string ctrlID, string guid,
743         int duration, string varyByParams, string varyByControls, string varyByCustom, string sqlDependency, 
744         BuildMethod buildMethod, string providerName) {
745
746         StaticPartialCachingControl pcc = new StaticPartialCachingControl(
747             ctrlID, guid, duration, varyByParams, varyByControls, varyByCustom, sqlDependency,
748             buildMethod, providerName);
749
750         ((IParserAccessor)parent).AddParsedSubObject(pcc);
751     }
752 }
753
754
755 /// <devdoc>
756 ///    <para>[To be supplied.]</para>
757 /// </devdoc>
758 public class PartialCachingControl : BasePartialCachingControl {
759
760     private IWebObjectFactory _objectFactory;
761     private Type _createCachedControlType;
762     private object[] _args;
763
764
765     public Control CachedControl { get { return _cachedCtrl; } }
766
767     internal PartialCachingControl(IWebObjectFactory objectFactory, Type createCachedControlType,
768         PartialCachingAttribute cacheAttrib, string cacheKey, object[] args) {
769         string providerName = cacheAttrib.ProviderName;
770         _ctrlID = cacheKey;
771         Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, cacheAttrib.Duration /*seconds*/);
772
773         SetVaryByParamsCollectionFromString(cacheAttrib.VaryByParams);
774
775         if (cacheAttrib.VaryByControls != null)
776             _varyByControlsCollection = cacheAttrib.VaryByControls.Split(varySeparator);
777         _varyByCustom = cacheAttrib.VaryByCustom;
778         _sqlDependency = cacheAttrib.SqlDependency;
779         if (providerName == OutputCache.ASPNET_INTERNAL_PROVIDER_NAME) {
780             providerName = null;
781         }
782         _provider = providerName;
783         _guid = cacheKey;
784         _objectFactory = objectFactory;
785         _createCachedControlType = createCachedControlType;
786         _args = args;
787     }
788
789     internal override Control CreateCachedControl() {
790
791         Control cachedControl;
792
793         if (_objectFactory != null) {
794             cachedControl = (Control) _objectFactory.CreateInstance();
795         }
796         else {
797             // Instantiate the control
798             cachedControl = (Control) HttpRuntime.CreatePublicInstance(_createCachedControlType, _args);
799         }
800
801         // If it's a user control, do some extra initialization
802         UserControl uc = cachedControl as UserControl;
803         if (uc != null)
804             uc.InitializeAsUserControl(Page);
805
806         cachedControl.ID = _ctrlID;
807
808         return cachedControl;
809     }
810 }
811
812 /*
813  * Holds param names that this cached item varies by.
814  */
815 [Serializable]
816 internal class ControlCachedVary {
817     private           Guid      _cachedVaryId;
818     internal readonly string[]  _varyByParams;
819     internal readonly string    _varyByCustom;
820     internal readonly string[]  _varyByControls;
821
822     internal Guid CachedVaryId { get { return _cachedVaryId; } }
823
824     internal ControlCachedVary(string[] varyByParams,
825         string[] varyByControls, string varyByCustom) {
826         _varyByParams = varyByParams;
827         _varyByControls = varyByControls;
828         _varyByCustom = varyByCustom;
829         _cachedVaryId = Guid.NewGuid();
830     }
831
832     public override bool Equals (Object obj) {
833
834         if (!(obj is ControlCachedVary))
835             return false;
836
837         ControlCachedVary cv = (ControlCachedVary) obj;
838
839         return  _varyByCustom == cv._varyByCustom               &&
840                 StringUtil.StringArrayEquals(_varyByParams, cv._varyByParams) &&
841                 StringUtil.StringArrayEquals(_varyByControls, cv._varyByControls);
842     }
843
844     public override int GetHashCode () {
845         HashCodeCombiner hashCodeCombiner = new HashCodeCombiner();
846         
847         // We need non-randomized hash code for _varyByCustom
848         hashCodeCombiner.AddInt(StringUtil.GetNonRandomizedHashCode(_varyByCustom));
849         
850         hashCodeCombiner.AddArray(_varyByParams);
851         hashCodeCombiner.AddArray(_varyByControls);
852         return hashCodeCombiner.CombinedHash32;
853     }
854 }
855
856 }
857