1 //------------------------------------------------------------------------------
2 // <copyright file="PartialCachingControl.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System.Web.UI {
12 using System.Collections;
13 using System.Collections.Specialized;
14 using System.ComponentModel;
15 using System.ComponentModel.Design;
16 using System.Globalization;
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;
27 // Keeps track of one call to Page Register* API
28 // The semantics of the fields depends to the call type
30 internal class RegisterCallData {
31 internal ClientAPIRegisterType Type;
32 internal ScriptKey Key;
33 internal string StringParam1;
34 internal string StringParam2;
35 internal string StringParam3;
38 // Data that we need to cache
40 internal class PartialCachingCacheEntry {
41 internal Guid _cachedVaryId;
42 internal string _dependenciesKey;
43 internal string[] _dependencies; // file dependencies
45 internal string OutputString;
46 internal string CssStyleString;
47 internal ArrayList RegisteredClientCalls;
51 /// <para>[To be supplied.]</para>
56 public abstract class BasePartialCachingControl : Control {
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;
79 internal const char varySeparator = ';';
80 internal const string varySeparatorString = ";";
82 internal override void InitRecursive(Control namingContainer) {
84 HashCodeCombiner combinedHashCode = new HashCodeCombiner();
86 _cacheKey = ComputeNonVaryCacheKey(combinedHashCode);
88 // Save the non-varying hash, so we don't need to recalculate it later
89 _nonVaryHashCode = combinedHashCode.CombinedHash;
91 PartialCachingCacheEntry cacheEntry = null;
93 // Check if there is a cache entry for the non-varying key
94 object tmpCacheEntry = OutputCache.GetFragment(_cacheKey, _provider);
96 if (tmpCacheEntry != null) {
97 ControlCachedVary cachedVary = tmpCacheEntry as ControlCachedVary;
98 if (cachedVary != null) {
99 string varyCachedKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
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) {
105 // explicitly remove the entry
106 OutputCache.RemoveFragment(varyCachedKey, _provider);
110 // If it wasn't a ControlCachedVary, it must be a PartialCachingCacheEntry
111 cacheEntry = (PartialCachingCacheEntry) tmpCacheEntry;
115 // If it's a cache miss, create the control and make it our child
116 if (cacheEntry == null) {
120 _cacheEntry = new PartialCachingCacheEntry();
122 _cachedCtrl = CreateCachedControl();
123 Controls.Add(_cachedCtrl);
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();
134 _outputString = cacheEntry.OutputString;
135 _cssStyleString = cacheEntry.CssStyleString;
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) {
143 case ClientAPIRegisterType.WebFormsScript:
144 Page.RegisterWebFormsScript();
147 case ClientAPIRegisterType.PostBackScript:
148 Page.RegisterPostBackScript();
151 case ClientAPIRegisterType.FocusScript:
152 Page.RegisterFocusScript();
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);
163 case ClientAPIRegisterType.OnSubmitStatement:
164 Page.ClientScript.RegisterOnSubmitStatementInternal(registerCallData.Key,
165 registerCallData.StringParam2);
168 case ClientAPIRegisterType.ArrayDeclaration:
169 Page.ClientScript.RegisterArrayDeclaration(registerCallData.StringParam1,
170 registerCallData.StringParam2);
173 case ClientAPIRegisterType.HiddenField:
174 Page.ClientScript.RegisterHiddenField(registerCallData.StringParam1,
175 registerCallData.StringParam2);
178 case ClientAPIRegisterType.ExpandoAttribute:
179 Page.ClientScript.RegisterExpandoAttribute(registerCallData.StringParam1,
180 registerCallData.StringParam2, registerCallData.StringParam3, false);
183 case ClientAPIRegisterType.EventValidation:
184 if (_registeredCallDataForEventValidation == null) {
185 _registeredCallDataForEventValidation = new ArrayList();
188 _registeredCallDataForEventValidation.Add(registerCallData);
197 base.InitRecursive(namingContainer);
201 internal override void LoadRecursive() {
203 // If we're in a cache hit, don't do anything special
204 if (_outputString != null) {
205 base.LoadRecursive();
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();
215 internal override void PreRenderRecursiveInternal() {
217 // If we're in a cache hit, don't do anything special
218 if (_outputString != null) {
219 base.PreRenderRecursiveInternal();
221 // register the cached styles on the Header control.
222 if (_cssStyleString != null && Page.Header != null) {
223 Page.Header.RegisterCssStyleString(_cssStyleString);
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();
237 public override void Dispose() {
238 if (_cacheDependency != null) {
239 _cacheDependency.Dispose();
240 _cacheDependency = null;
246 internal abstract Control CreateCachedControl();
250 /// <para> Gets or sets the CacheDependency used to cache the control output.</para>
252 public CacheDependency Dependency {
253 get { return _cacheDependency; }
254 set { _cacheDependency = value; }
258 public ControlCachePolicy CachePolicy {
260 // Create the ControlCachePolicy object on demand
261 if (_cachePolicy == null)
262 _cachePolicy = new ControlCachePolicy(this);
268 internal HttpCacheVaryByParams VaryByParams {
270 if (_varyByParamsCollection == null) {
271 _varyByParamsCollection = new HttpCacheVaryByParams();
272 _varyByParamsCollection.IgnoreParams = true;
275 return _varyByParamsCollection;
279 internal string VaryByControl {
281 if (_varyByControlsCollection == null)
284 return String.Join(varySeparatorString, _varyByControlsCollection);
288 if (String.IsNullOrEmpty(value)) {
289 _varyByControlsCollection = null;
292 _varyByControlsCollection = value.Split(varySeparator);
297 internal TimeSpan Duration {
299 // Special case MaxValue
300 if (_utcExpirationTime == DateTime.MaxValue)
301 return TimeSpan.MaxValue;
303 return _utcExpirationTime - DateTime.UtcNow;
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;
313 // Compute the expiration time
314 _utcExpirationTime = DateTime.UtcNow.Add(value);
319 private void RegisterValidationEvents() {
320 if (_registeredCallDataForEventValidation != null) {
321 foreach (RegisterCallData registerCallData in _registeredCallDataForEventValidation) {
322 Page.ClientScript.RegisterForEventValidation(registerCallData.StringParam1,
323 registerCallData.StringParam2);
328 internal void RegisterStyleInfo(SelectorStyleInfo selectorInfo) {
329 if (_registeredStyleInfo == null) {
330 _registeredStyleInfo = new ArrayList();
333 _registeredStyleInfo.Add(selectorInfo);
338 /// <para>[To be supplied.]</para>
340 protected internal override void Render(HtmlTextWriter output) {
341 CacheDependency sqlCacheDep = null;
343 // If the output is cached, use it and do nothing else
344 if (_outputString != null) {
345 output.Write(_outputString);
346 RegisterValidationEvents();
350 // If caching was turned off, just render the control
351 if (_cachingDisabled || !RuntimeConfig.GetAppConfig().OutputCache.EnableFragmentCache) {
352 _cachedCtrl.RenderControl(output);
356 // Create SQL cache dependency before we render the page
357 if (_sqlDependency != null) {
358 sqlCacheDep = SqlCacheDependency.CreateOutputCacheDependency(_sqlDependency);
361 _cacheEntry.CssStyleString = GetCssStyleRenderString(output.GetType());
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);
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();
376 Context.Response.SwitchWriter(savedWriter);
379 _cacheEntry.OutputString = tmpWriter.ToString();
381 // Send the output to the response
382 output.Write(_cacheEntry.OutputString);
386 cacheDep = _cacheDependency;
388 if (sqlCacheDep != null) {
389 if (cacheDep == null) {
390 cacheDep = sqlCacheDep;
393 AggregateCacheDependency aggr = new AggregateCacheDependency();
396 aggr.Add(sqlCacheDep);
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;
408 string[] varyByParams = null;
409 if (_varyByParamsCollection != null)
410 varyByParams = _varyByParamsCollection.GetParams();
412 cachedVary = new ControlCachedVary(varyByParams, _varyByControlsCollection, _varyByCustom);
414 HashCodeCombiner combinedHashCode = new HashCodeCombiner(_nonVaryHashCode);
415 realItemCacheKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
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;
426 utcExpirationTime = _utcExpirationTime;
427 slidingExpiration = Cache.NoSlidingExpiration;
431 OutputCache.InsertFragment(_cacheKey, cachedVary,
432 realItemCacheKey, _cacheEntry,
433 cacheDep /*dependencies*/,
434 utcExpirationTime, slidingExpiration,
438 if (cacheDep != null) {
445 // Return the key used to cache the output
446 private string ComputeNonVaryCacheKey(HashCodeCombiner combinedHashCode) {
447 // Create a cache key by combining various elements
449 // Start with the guid
450 combinedHashCode.AddObject(_guid);
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);
457 return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
460 private string ComputeVaryCacheKey(HashCodeCombiner combinedHashCode,
461 ControlCachedVary cachedVary) {
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);
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);
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*/);
486 if (cachedVary._varyByParams != null) {
488 ICollection itemsToUseForHashCode;
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;
495 itemsToUseForHashCode = cachedVary._varyByParams;
497 // Add the items and their values to compute the hash code
498 foreach (string varyByParam in itemsToUseForHashCode) {
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)
503 combinedHashCode.AddCaseInsensitiveString(varyByParam);
504 string val = reqValCollection[varyByParam];
506 combinedHashCode.AddObject(val);
510 if (cachedVary._varyByControls != null) {
512 // Prepend them with a prefix to make them fully qualified
514 if (NamingContainer == Page) {
515 // No prefix if it's the page
516 prefix = String.Empty;
519 prefix = NamingContainer.UniqueID;
520 Debug.Assert(!String.IsNullOrEmpty(prefix));
521 prefix += IdSeparator;
524 prefix += _ctrlID + IdSeparator;
526 // Add all the relative vary params and their values to the hash code
527 foreach (string varyByParam in cachedVary._varyByControls) {
529 string temp = prefix + varyByParam.Trim();
530 combinedHashCode.AddCaseInsensitiveString(temp);
531 string val = reqValCollection[temp];
533 combinedHashCode.AddObject(reqValCollection[temp]);
537 if (cachedVary._varyByCustom != null) {
538 string customString = Context.ApplicationInstance.GetVaryByCustomString(
539 Context, cachedVary._varyByCustom);
540 if (customString != null)
541 combinedHashCode.AddObject(customString);
544 return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
547 private string GetCssStyleRenderString(Type htmlTextWriterType) {
548 // Nothing to do if no styles are registered.
549 if (_registeredStyleInfo == null) {
553 // Create an empty cssStringWriter
554 StringWriter cssStringWriter = new StringWriter(CultureInfo.CurrentCulture);
556 // Create a new HtmlTextWriter, with the same type as the current one
557 HtmlTextWriter cssHtmlTextWriter =
558 Page.CreateHtmlTextWriterFromType(cssStringWriter, htmlTextWriterType);
560 CssTextWriter cssWriter = new CssTextWriter(cssHtmlTextWriter);
562 foreach (SelectorStyleInfo si in _registeredStyleInfo) {
563 HtmlHead.RenderCssRule(cssWriter, si.selector, si.style, si.urlResolver);
566 // Return the css style rendered string
567 return cssStringWriter.ToString();
570 internal void SetVaryByParamsCollectionFromString(string varyByParams) {
572 Debug.Assert(_varyByParamsCollection == null);
574 if (varyByParams == null)
577 string[] varyByParamsStrings = varyByParams.Split(varySeparator);
578 _varyByParamsCollection = new HttpCacheVaryByParams();
579 _varyByParamsCollection.ResetFromParams(varyByParamsStrings);
582 internal void RegisterPostBackScript() {
583 RegisterClientCall(ClientAPIRegisterType.PostBackScript, String.Empty, null);
586 internal void RegisterFocusScript() {
587 RegisterClientCall(ClientAPIRegisterType.FocusScript, String.Empty, null);
590 internal void RegisterWebFormsScript() {
591 RegisterClientCall(ClientAPIRegisterType.WebFormsScript, String.Empty, null);
594 private void RegisterClientCall(ClientAPIRegisterType type,
595 ScriptKey scriptKey, string stringParam2) {
597 // Keep track of the call, in order to be able to call it again when there is a cache hit.
599 RegisterCallData registerCallData = new RegisterCallData();
600 registerCallData.Type = type;
601 registerCallData.Key = scriptKey;
602 registerCallData.StringParam2 = stringParam2;
604 if (_cacheEntry.RegisteredClientCalls == null)
605 _cacheEntry.RegisteredClientCalls = new ArrayList();
607 _cacheEntry.RegisteredClientCalls.Add(registerCallData);
610 private void RegisterClientCall(ClientAPIRegisterType type,
611 string stringParam1, string stringParam2) {
612 RegisterClientCall(type, stringParam1, stringParam2, null);
615 private void RegisterClientCall(ClientAPIRegisterType type,
616 string stringParam1, string stringParam2, string stringParam3) {
618 // Keep track of the call, in order to be able to call it again when there is a cache hit.
620 RegisterCallData registerCallData = new RegisterCallData();
621 registerCallData.Type = type;
622 registerCallData.StringParam1 = stringParam1;
623 registerCallData.StringParam2 = stringParam2;
624 registerCallData.StringParam3 = stringParam3;
626 if (_cacheEntry.RegisteredClientCalls == null)
627 _cacheEntry.RegisteredClientCalls = new ArrayList();
629 _cacheEntry.RegisteredClientCalls.Add(registerCallData);
632 internal void RegisterScriptBlock(ClientAPIRegisterType type, ScriptKey key, string script) {
633 RegisterClientCall(type, key, script);
636 internal void RegisterOnSubmitStatement(ScriptKey key, string script) {
637 RegisterClientCall(ClientAPIRegisterType.OnSubmitStatement, key, script);
640 internal void RegisterArrayDeclaration(string arrayName, string arrayValue) {
641 RegisterClientCall(ClientAPIRegisterType.ArrayDeclaration,
642 arrayName, arrayValue);
645 internal void RegisterHiddenField(string hiddenFieldName, string hiddenFieldInitialValue) {
646 RegisterClientCall(ClientAPIRegisterType.HiddenField,
647 hiddenFieldName, hiddenFieldInitialValue);
650 internal void RegisterExpandoAttribute(string controlID, string attributeName, string attributeValue) {
651 RegisterClientCall(ClientAPIRegisterType.ExpandoAttribute, controlID, attributeName, attributeValue);
654 internal void RegisterForEventValidation(string uniqueID, string argument) {
655 RegisterClientCall(ClientAPIRegisterType.EventValidation, uniqueID, argument);
661 /// <para>[To be supplied.]</para>
663 public class StaticPartialCachingControl : BasePartialCachingControl {
665 private BuildMethod _buildMethod;
670 /// <para>[To be supplied.]</para>
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)
683 /// <para>[To be supplied.]</para>
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)
693 public StaticPartialCachingControl(string ctrlID, string guid, int duration,
694 string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
695 BuildMethod buildMethod, string providerName) {
697 Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, duration /*seconds*/);
699 SetVaryByParamsCollectionFromString(varyByParams);
701 if (varyByControls != null)
702 _varyByControlsCollection = varyByControls.Split(varySeparator);
703 _varyByCustom = varyByCustom;
705 _buildMethod = buildMethod;
706 _sqlDependency = sqlDependency;
707 _provider = providerName;
710 internal override Control CreateCachedControl() {
711 return _buildMethod();
715 * Called by generated code (hence must be public).
716 * Create a StaticPartialCachingControl and add it as a child
721 /// <para>[To be supplied.]</para>
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);
733 /// <para>[To be supplied.]</para>
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);
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) {
746 StaticPartialCachingControl pcc = new StaticPartialCachingControl(
747 ctrlID, guid, duration, varyByParams, varyByControls, varyByCustom, sqlDependency,
748 buildMethod, providerName);
750 ((IParserAccessor)parent).AddParsedSubObject(pcc);
756 /// <para>[To be supplied.]</para>
758 public class PartialCachingControl : BasePartialCachingControl {
760 private IWebObjectFactory _objectFactory;
761 private Type _createCachedControlType;
762 private object[] _args;
765 public Control CachedControl { get { return _cachedCtrl; } }
767 internal PartialCachingControl(IWebObjectFactory objectFactory, Type createCachedControlType,
768 PartialCachingAttribute cacheAttrib, string cacheKey, object[] args) {
769 string providerName = cacheAttrib.ProviderName;
771 Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, cacheAttrib.Duration /*seconds*/);
773 SetVaryByParamsCollectionFromString(cacheAttrib.VaryByParams);
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) {
782 _provider = providerName;
784 _objectFactory = objectFactory;
785 _createCachedControlType = createCachedControlType;
789 internal override Control CreateCachedControl() {
791 Control cachedControl;
793 if (_objectFactory != null) {
794 cachedControl = (Control) _objectFactory.CreateInstance();
797 // Instantiate the control
798 cachedControl = (Control) HttpRuntime.CreatePublicInstance(_createCachedControlType, _args);
801 // If it's a user control, do some extra initialization
802 UserControl uc = cachedControl as UserControl;
804 uc.InitializeAsUserControl(Page);
806 cachedControl.ID = _ctrlID;
808 return cachedControl;
813 * Holds param names that this cached item varies by.
816 internal class ControlCachedVary {
817 private Guid _cachedVaryId;
818 internal readonly string[] _varyByParams;
819 internal readonly string _varyByCustom;
820 internal readonly string[] _varyByControls;
822 internal Guid CachedVaryId { get { return _cachedVaryId; } }
824 internal ControlCachedVary(string[] varyByParams,
825 string[] varyByControls, string varyByCustom) {
826 _varyByParams = varyByParams;
827 _varyByControls = varyByControls;
828 _varyByCustom = varyByCustom;
829 _cachedVaryId = Guid.NewGuid();
832 public override bool Equals (Object obj) {
834 if (!(obj is ControlCachedVary))
837 ControlCachedVary cv = (ControlCachedVary) obj;
839 return _varyByCustom == cv._varyByCustom &&
840 StringUtil.StringArrayEquals(_varyByParams, cv._varyByParams) &&
841 StringUtil.StringArrayEquals(_varyByControls, cv._varyByControls);
844 public override int GetHashCode () {
845 HashCodeCombiner hashCodeCombiner = new HashCodeCombiner();
847 // We need non-randomized hash code for _varyByCustom
848 hashCodeCombiner.AddInt(StringUtil.GetNonRandomizedHashCode(_varyByCustom));
850 hashCodeCombiner.AddArray(_varyByParams);
851 hashCodeCombiner.AddArray(_varyByControls);
852 return hashCodeCombiner.CombinedHash32;