94b2861243
Former-commit-id: 5f9c6ae75f295e057a7d2971f3a6df4656fa8850
858 lines
31 KiB
C#
858 lines
31 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="PartialCachingControl.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.UI {
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Globalization;
|
|
using System.Web;
|
|
using System.Web.Util;
|
|
using System.Web.UI.HtmlControls;
|
|
using System.Web.UI.WebControls;
|
|
using System.Web.Caching;
|
|
using System.Web.Compilation;
|
|
using System.Web.Configuration;
|
|
using System.Security.Permissions;
|
|
|
|
|
|
// Keeps track of one call to Page Register* API
|
|
// The semantics of the fields depends to the call type
|
|
[Serializable]
|
|
internal class RegisterCallData {
|
|
internal ClientAPIRegisterType Type;
|
|
internal ScriptKey Key;
|
|
internal string StringParam1;
|
|
internal string StringParam2;
|
|
internal string StringParam3;
|
|
}
|
|
|
|
// Data that we need to cache
|
|
[Serializable]
|
|
internal class PartialCachingCacheEntry {
|
|
internal Guid _cachedVaryId;
|
|
internal string _dependenciesKey;
|
|
internal string[] _dependencies; // file dependencies
|
|
|
|
internal string OutputString;
|
|
internal string CssStyleString;
|
|
internal ArrayList RegisteredClientCalls;
|
|
}
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
[
|
|
ToolboxItem(false)
|
|
]
|
|
public abstract class BasePartialCachingControl : Control {
|
|
|
|
internal Control _cachedCtrl;
|
|
private long _nonVaryHashCode;
|
|
internal string _ctrlID;
|
|
internal string _guid;
|
|
internal DateTime _utcExpirationTime;
|
|
internal bool _useSlidingExpiration;
|
|
internal HttpCacheVaryByParams _varyByParamsCollection;
|
|
internal string[] _varyByControlsCollection;
|
|
internal string _varyByCustom;
|
|
internal string _sqlDependency;
|
|
internal string _provider;
|
|
internal bool _cachingDisabled;
|
|
private string _outputString;
|
|
private string _cssStyleString;
|
|
private string _cacheKey;
|
|
private CacheDependency _cacheDependency;
|
|
private PartialCachingCacheEntry _cacheEntry;
|
|
private ControlCachePolicy _cachePolicy;
|
|
private ArrayList _registeredCallDataForEventValidation;
|
|
private ArrayList _registeredStyleInfo = null;
|
|
|
|
internal const char varySeparator = ';';
|
|
internal const string varySeparatorString = ";";
|
|
|
|
internal override void InitRecursive(Control namingContainer) {
|
|
|
|
HashCodeCombiner combinedHashCode = new HashCodeCombiner();
|
|
|
|
_cacheKey = ComputeNonVaryCacheKey(combinedHashCode);
|
|
|
|
// Save the non-varying hash, so we don't need to recalculate it later
|
|
_nonVaryHashCode = combinedHashCode.CombinedHash;
|
|
|
|
PartialCachingCacheEntry cacheEntry = null;
|
|
|
|
// Check if there is a cache entry for the non-varying key
|
|
object tmpCacheEntry = OutputCache.GetFragment(_cacheKey, _provider);
|
|
|
|
if (tmpCacheEntry != null) {
|
|
ControlCachedVary cachedVary = tmpCacheEntry as ControlCachedVary;
|
|
if (cachedVary != null) {
|
|
string varyCachedKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
|
|
|
|
// Check if there is a cache entry for the varying key
|
|
cacheEntry = (PartialCachingCacheEntry) OutputCache.GetFragment(varyCachedKey, _provider);
|
|
if (cacheEntry != null && cacheEntry._cachedVaryId != cachedVary.CachedVaryId) {
|
|
cacheEntry = null;
|
|
// explicitly remove the entry
|
|
OutputCache.RemoveFragment(varyCachedKey, _provider);
|
|
}
|
|
}
|
|
else {
|
|
// If it wasn't a ControlCachedVary, it must be a PartialCachingCacheEntry
|
|
cacheEntry = (PartialCachingCacheEntry) tmpCacheEntry;
|
|
}
|
|
}
|
|
|
|
// If it's a cache miss, create the control and make it our child
|
|
if (cacheEntry == null) {
|
|
|
|
// Cache miss
|
|
|
|
_cacheEntry = new PartialCachingCacheEntry();
|
|
|
|
_cachedCtrl = CreateCachedControl();
|
|
Controls.Add(_cachedCtrl);
|
|
|
|
// Make sure the Page knows about us while the control's OnInit is called
|
|
Page.PushCachingControl(this);
|
|
base.InitRecursive(namingContainer);
|
|
Page.PopCachingControl();
|
|
}
|
|
else {
|
|
|
|
// Cache hit
|
|
|
|
_outputString = cacheEntry.OutputString;
|
|
_cssStyleString = cacheEntry.CssStyleString;
|
|
|
|
// If any calls to Register* API's were made when the control was run,
|
|
// make them now to restore correct behavior (VSWhidbey 80907)
|
|
if (cacheEntry.RegisteredClientCalls != null) {
|
|
foreach (RegisterCallData registerCallData in cacheEntry.RegisteredClientCalls) {
|
|
switch (registerCallData.Type) {
|
|
|
|
case ClientAPIRegisterType.WebFormsScript:
|
|
Page.RegisterWebFormsScript();
|
|
break;
|
|
|
|
case ClientAPIRegisterType.PostBackScript:
|
|
Page.RegisterPostBackScript();
|
|
break;
|
|
|
|
case ClientAPIRegisterType.FocusScript:
|
|
Page.RegisterFocusScript();
|
|
break;
|
|
|
|
case ClientAPIRegisterType.ClientScriptBlocks:
|
|
case ClientAPIRegisterType.ClientScriptBlocksWithoutTags:
|
|
case ClientAPIRegisterType.ClientStartupScripts:
|
|
case ClientAPIRegisterType.ClientStartupScriptsWithoutTags:
|
|
Page.ClientScript.RegisterScriptBlock(registerCallData.Key,
|
|
registerCallData.StringParam2, registerCallData.Type);
|
|
break;
|
|
|
|
case ClientAPIRegisterType.OnSubmitStatement:
|
|
Page.ClientScript.RegisterOnSubmitStatementInternal(registerCallData.Key,
|
|
registerCallData.StringParam2);
|
|
break;
|
|
|
|
case ClientAPIRegisterType.ArrayDeclaration:
|
|
Page.ClientScript.RegisterArrayDeclaration(registerCallData.StringParam1,
|
|
registerCallData.StringParam2);
|
|
break;
|
|
|
|
case ClientAPIRegisterType.HiddenField:
|
|
Page.ClientScript.RegisterHiddenField(registerCallData.StringParam1,
|
|
registerCallData.StringParam2);
|
|
break;
|
|
|
|
case ClientAPIRegisterType.ExpandoAttribute:
|
|
Page.ClientScript.RegisterExpandoAttribute(registerCallData.StringParam1,
|
|
registerCallData.StringParam2, registerCallData.StringParam3, false);
|
|
break;
|
|
|
|
case ClientAPIRegisterType.EventValidation:
|
|
if (_registeredCallDataForEventValidation == null) {
|
|
_registeredCallDataForEventValidation = new ArrayList();
|
|
}
|
|
|
|
_registeredCallDataForEventValidation.Add(registerCallData);
|
|
break;
|
|
default:
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
base.InitRecursive(namingContainer);
|
|
}
|
|
}
|
|
|
|
internal override void LoadRecursive() {
|
|
|
|
// If we're in a cache hit, don't do anything special
|
|
if (_outputString != null) {
|
|
base.LoadRecursive();
|
|
return;
|
|
}
|
|
|
|
// Make sure the Page knows about us while the control's OnLoad is called
|
|
Page.PushCachingControl(this);
|
|
base.LoadRecursive();
|
|
Page.PopCachingControl();
|
|
}
|
|
|
|
internal override void PreRenderRecursiveInternal() {
|
|
|
|
// If we're in a cache hit, don't do anything special
|
|
if (_outputString != null) {
|
|
base.PreRenderRecursiveInternal();
|
|
|
|
// register the cached styles on the Header control.
|
|
if (_cssStyleString != null && Page.Header != null) {
|
|
Page.Header.RegisterCssStyleString(_cssStyleString);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Make sure the Page knows about us while the control's OnPreRender is called
|
|
Page.PushCachingControl(this);
|
|
base.PreRenderRecursiveInternal();
|
|
Page.PopCachingControl();
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
public override void Dispose() {
|
|
if (_cacheDependency != null) {
|
|
_cacheDependency.Dispose();
|
|
_cacheDependency = null;
|
|
}
|
|
|
|
base.Dispose();
|
|
}
|
|
|
|
internal abstract Control CreateCachedControl();
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para> Gets or sets the CacheDependency used to cache the control output.</para>
|
|
/// </devdoc>
|
|
public CacheDependency Dependency {
|
|
get { return _cacheDependency; }
|
|
set { _cacheDependency = value; }
|
|
}
|
|
|
|
|
|
public ControlCachePolicy CachePolicy {
|
|
get {
|
|
// Create the ControlCachePolicy object on demand
|
|
if (_cachePolicy == null)
|
|
_cachePolicy = new ControlCachePolicy(this);
|
|
|
|
return _cachePolicy;
|
|
}
|
|
}
|
|
|
|
internal HttpCacheVaryByParams VaryByParams {
|
|
get {
|
|
if (_varyByParamsCollection == null) {
|
|
_varyByParamsCollection = new HttpCacheVaryByParams();
|
|
_varyByParamsCollection.IgnoreParams = true;
|
|
}
|
|
|
|
return _varyByParamsCollection;
|
|
}
|
|
}
|
|
|
|
internal string VaryByControl {
|
|
get {
|
|
if (_varyByControlsCollection == null)
|
|
return String.Empty;
|
|
|
|
return String.Join(varySeparatorString, _varyByControlsCollection);
|
|
}
|
|
|
|
set {
|
|
if (String.IsNullOrEmpty(value)) {
|
|
_varyByControlsCollection = null;
|
|
}
|
|
else {
|
|
_varyByControlsCollection = value.Split(varySeparator);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal TimeSpan Duration {
|
|
get {
|
|
// Special case MaxValue
|
|
if (_utcExpirationTime == DateTime.MaxValue)
|
|
return TimeSpan.MaxValue;
|
|
|
|
return _utcExpirationTime - DateTime.UtcNow;
|
|
}
|
|
|
|
set {
|
|
if (value == TimeSpan.MaxValue) {
|
|
// If it's the max timespan, just make it DateTime.MaxValue to avoid
|
|
// an overflow when adding (VSWhidbey 273271)
|
|
_utcExpirationTime = DateTime.MaxValue;
|
|
}
|
|
else {
|
|
// Compute the expiration time
|
|
_utcExpirationTime = DateTime.UtcNow.Add(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void RegisterValidationEvents() {
|
|
if (_registeredCallDataForEventValidation != null) {
|
|
foreach (RegisterCallData registerCallData in _registeredCallDataForEventValidation) {
|
|
Page.ClientScript.RegisterForEventValidation(registerCallData.StringParam1,
|
|
registerCallData.StringParam2);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void RegisterStyleInfo(SelectorStyleInfo selectorInfo) {
|
|
if (_registeredStyleInfo == null) {
|
|
_registeredStyleInfo = new ArrayList();
|
|
}
|
|
|
|
_registeredStyleInfo.Add(selectorInfo);
|
|
}
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
protected internal override void Render(HtmlTextWriter output) {
|
|
CacheDependency sqlCacheDep = null;
|
|
|
|
// If the output is cached, use it and do nothing else
|
|
if (_outputString != null) {
|
|
output.Write(_outputString);
|
|
RegisterValidationEvents();
|
|
return;
|
|
}
|
|
|
|
// If caching was turned off, just render the control
|
|
if (_cachingDisabled || !RuntimeConfig.GetAppConfig().OutputCache.EnableFragmentCache) {
|
|
_cachedCtrl.RenderControl(output);
|
|
return;
|
|
}
|
|
|
|
// Create SQL cache dependency before we render the page
|
|
if (_sqlDependency != null) {
|
|
sqlCacheDep = SqlCacheDependency.CreateOutputCacheDependency(_sqlDependency);
|
|
}
|
|
|
|
_cacheEntry.CssStyleString = GetCssStyleRenderString(output.GetType());
|
|
|
|
// Create a new HtmlTextWriter, with the same type as the current one (see ASURT 118922)
|
|
StringWriter tmpWriter = new StringWriter();
|
|
HtmlTextWriter tmpHtmlWriter = Page.CreateHtmlTextWriterFromType(tmpWriter, output.GetType());
|
|
CacheDependency cacheDep;
|
|
TextWriter savedWriter = Context.Response.SwitchWriter(tmpWriter);
|
|
|
|
try {
|
|
// Make sure the Page knows about us while the control's OnPreRender is called
|
|
Page.PushCachingControl(this);
|
|
_cachedCtrl.RenderControl(tmpHtmlWriter);
|
|
Page.PopCachingControl();
|
|
}
|
|
finally {
|
|
Context.Response.SwitchWriter(savedWriter);
|
|
}
|
|
|
|
_cacheEntry.OutputString = tmpWriter.ToString();
|
|
|
|
// Send the output to the response
|
|
output.Write(_cacheEntry.OutputString);
|
|
|
|
// Cache the output
|
|
|
|
cacheDep = _cacheDependency;
|
|
|
|
if (sqlCacheDep != null) {
|
|
if (cacheDep == null) {
|
|
cacheDep = sqlCacheDep;
|
|
}
|
|
else {
|
|
AggregateCacheDependency aggr = new AggregateCacheDependency();
|
|
|
|
aggr.Add(cacheDep);
|
|
aggr.Add(sqlCacheDep);
|
|
cacheDep = aggr;
|
|
}
|
|
}
|
|
|
|
ControlCachedVary cachedVary = null;
|
|
string realItemCacheKey;
|
|
// If there are no varies, use the non-varying key
|
|
if (_varyByParamsCollection == null && _varyByControlsCollection == null && _varyByCustom == null) {
|
|
realItemCacheKey = _cacheKey;
|
|
}
|
|
else {
|
|
string[] varyByParams = null;
|
|
if (_varyByParamsCollection != null)
|
|
varyByParams = _varyByParamsCollection.GetParams();
|
|
|
|
cachedVary = new ControlCachedVary(varyByParams, _varyByControlsCollection, _varyByCustom);
|
|
|
|
HashCodeCombiner combinedHashCode = new HashCodeCombiner(_nonVaryHashCode);
|
|
realItemCacheKey = ComputeVaryCacheKey(combinedHashCode, cachedVary);
|
|
}
|
|
|
|
// Compute the correct expiration, sliding or absolute
|
|
DateTime utcExpirationTime;
|
|
TimeSpan slidingExpiration;
|
|
if (_useSlidingExpiration) {
|
|
utcExpirationTime = Cache.NoAbsoluteExpiration;
|
|
slidingExpiration = _utcExpirationTime - DateTime.UtcNow;
|
|
}
|
|
else {
|
|
utcExpirationTime = _utcExpirationTime;
|
|
slidingExpiration = Cache.NoSlidingExpiration;
|
|
}
|
|
|
|
try {
|
|
OutputCache.InsertFragment(_cacheKey, cachedVary,
|
|
realItemCacheKey, _cacheEntry,
|
|
cacheDep /*dependencies*/,
|
|
utcExpirationTime, slidingExpiration,
|
|
_provider);
|
|
}
|
|
catch {
|
|
if (cacheDep != null) {
|
|
cacheDep.Dispose();
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// Return the key used to cache the output
|
|
private string ComputeNonVaryCacheKey(HashCodeCombiner combinedHashCode) {
|
|
// Create a cache key by combining various elements
|
|
|
|
// Start with the guid
|
|
combinedHashCode.AddObject(_guid);
|
|
|
|
// Make the key vary based on the type of the writer (ASURT 118922)
|
|
HttpBrowserCapabilities browserCap = Context.Request.Browser;
|
|
if (browserCap != null)
|
|
combinedHashCode.AddObject(browserCap.TagWriter);
|
|
|
|
return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
|
|
}
|
|
|
|
private string ComputeVaryCacheKey(HashCodeCombiner combinedHashCode,
|
|
ControlCachedVary cachedVary) {
|
|
|
|
// Add something to the has to differentiate it from the non-vary hash.
|
|
// This is needed in case this method doesn't add anything else to the hash (VSWhidbey 194199)
|
|
combinedHashCode.AddInt(1);
|
|
|
|
// Get the request value collection
|
|
NameValueCollection reqValCollection;
|
|
HttpRequest request = Page.Request;
|
|
if (request != null && request.HttpVerb == HttpVerb.POST) {
|
|
// Bug 6129: Partial cache key should include posted form values in postbacks.
|
|
// Include both QueryString and Form values (but not Cookies or Server Variables like Request.Params does).
|
|
// Per Request.Params behavior, add QueryString values before Form values
|
|
reqValCollection = new NameValueCollection(request.QueryString);
|
|
reqValCollection.Add(request.Form);
|
|
}
|
|
else {
|
|
// Use the existing value if possible to avoid recreating a NameValueCollection
|
|
reqValCollection = Page.RequestValueCollection;
|
|
// If it's not set, get it based on the method
|
|
if (reqValCollection == null) {
|
|
reqValCollection = Page.GetCollectionBasedOnMethod(true /*dontReturnNull*/);
|
|
}
|
|
}
|
|
|
|
if (cachedVary._varyByParams != null) {
|
|
|
|
ICollection itemsToUseForHashCode;
|
|
|
|
// If '*' was specified, use all the items in the request collection.
|
|
// Otherwise, use only those specified.
|
|
if (cachedVary._varyByParams.Length == 1 && cachedVary._varyByParams[0] == "*")
|
|
itemsToUseForHashCode = reqValCollection;
|
|
else
|
|
itemsToUseForHashCode = cachedVary._varyByParams;
|
|
|
|
// Add the items and their values to compute the hash code
|
|
foreach (string varyByParam in itemsToUseForHashCode) {
|
|
|
|
// Note: we use to ignore certain system fields here (like VIEWSTATE), but decided
|
|
// not to for consistency with pahe output caching (VSWhidbey 196267, 479252)
|
|
|
|
combinedHashCode.AddCaseInsensitiveString(varyByParam);
|
|
string val = reqValCollection[varyByParam];
|
|
if (val != null)
|
|
combinedHashCode.AddObject(val);
|
|
}
|
|
}
|
|
|
|
if (cachedVary._varyByControls != null) {
|
|
|
|
// Prepend them with a prefix to make them fully qualified
|
|
string prefix;
|
|
if (NamingContainer == Page) {
|
|
// No prefix if it's the page
|
|
prefix = String.Empty;
|
|
}
|
|
else {
|
|
prefix = NamingContainer.UniqueID;
|
|
Debug.Assert(!String.IsNullOrEmpty(prefix));
|
|
prefix += IdSeparator;
|
|
}
|
|
|
|
prefix += _ctrlID + IdSeparator;
|
|
|
|
// Add all the relative vary params and their values to the hash code
|
|
foreach (string varyByParam in cachedVary._varyByControls) {
|
|
|
|
string temp = prefix + varyByParam.Trim();
|
|
combinedHashCode.AddCaseInsensitiveString(temp);
|
|
string val = reqValCollection[temp];
|
|
if (val != null)
|
|
combinedHashCode.AddObject(reqValCollection[temp]);
|
|
}
|
|
}
|
|
|
|
if (cachedVary._varyByCustom != null) {
|
|
string customString = Context.ApplicationInstance.GetVaryByCustomString(
|
|
Context, cachedVary._varyByCustom);
|
|
if (customString != null)
|
|
combinedHashCode.AddObject(customString);
|
|
}
|
|
|
|
return CacheInternal.PrefixPartialCachingControl + combinedHashCode.CombinedHashString;
|
|
}
|
|
|
|
private string GetCssStyleRenderString(Type htmlTextWriterType) {
|
|
// Nothing to do if no styles are registered.
|
|
if (_registeredStyleInfo == null) {
|
|
return null;
|
|
}
|
|
|
|
// Create an empty cssStringWriter
|
|
StringWriter cssStringWriter = new StringWriter(CultureInfo.CurrentCulture);
|
|
|
|
// Create a new HtmlTextWriter, with the same type as the current one
|
|
HtmlTextWriter cssHtmlTextWriter =
|
|
Page.CreateHtmlTextWriterFromType(cssStringWriter, htmlTextWriterType);
|
|
|
|
CssTextWriter cssWriter = new CssTextWriter(cssHtmlTextWriter);
|
|
|
|
foreach (SelectorStyleInfo si in _registeredStyleInfo) {
|
|
HtmlHead.RenderCssRule(cssWriter, si.selector, si.style, si.urlResolver);
|
|
}
|
|
|
|
// Return the css style rendered string
|
|
return cssStringWriter.ToString();
|
|
}
|
|
|
|
internal void SetVaryByParamsCollectionFromString(string varyByParams) {
|
|
|
|
Debug.Assert(_varyByParamsCollection == null);
|
|
|
|
if (varyByParams == null)
|
|
return;
|
|
|
|
string[] varyByParamsStrings = varyByParams.Split(varySeparator);
|
|
_varyByParamsCollection = new HttpCacheVaryByParams();
|
|
_varyByParamsCollection.SetParams(varyByParamsStrings);
|
|
}
|
|
|
|
internal void RegisterPostBackScript() {
|
|
RegisterClientCall(ClientAPIRegisterType.PostBackScript, String.Empty, null);
|
|
}
|
|
|
|
internal void RegisterFocusScript() {
|
|
RegisterClientCall(ClientAPIRegisterType.FocusScript, String.Empty, null);
|
|
}
|
|
|
|
internal void RegisterWebFormsScript() {
|
|
RegisterClientCall(ClientAPIRegisterType.WebFormsScript, String.Empty, null);
|
|
}
|
|
|
|
private void RegisterClientCall(ClientAPIRegisterType type,
|
|
ScriptKey scriptKey, string stringParam2) {
|
|
|
|
// Keep track of the call, in order to be able to call it again when there is a cache hit.
|
|
|
|
RegisterCallData registerCallData = new RegisterCallData();
|
|
registerCallData.Type = type;
|
|
registerCallData.Key = scriptKey;
|
|
registerCallData.StringParam2 = stringParam2;
|
|
|
|
if (_cacheEntry.RegisteredClientCalls == null)
|
|
_cacheEntry.RegisteredClientCalls = new ArrayList();
|
|
|
|
_cacheEntry.RegisteredClientCalls.Add(registerCallData);
|
|
}
|
|
|
|
private void RegisterClientCall(ClientAPIRegisterType type,
|
|
string stringParam1, string stringParam2) {
|
|
RegisterClientCall(type, stringParam1, stringParam2, null);
|
|
}
|
|
|
|
private void RegisterClientCall(ClientAPIRegisterType type,
|
|
string stringParam1, string stringParam2, string stringParam3) {
|
|
|
|
// Keep track of the call, in order to be able to call it again when there is a cache hit.
|
|
|
|
RegisterCallData registerCallData = new RegisterCallData();
|
|
registerCallData.Type = type;
|
|
registerCallData.StringParam1 = stringParam1;
|
|
registerCallData.StringParam2 = stringParam2;
|
|
registerCallData.StringParam3 = stringParam3;
|
|
|
|
if (_cacheEntry.RegisteredClientCalls == null)
|
|
_cacheEntry.RegisteredClientCalls = new ArrayList();
|
|
|
|
_cacheEntry.RegisteredClientCalls.Add(registerCallData);
|
|
}
|
|
|
|
internal void RegisterScriptBlock(ClientAPIRegisterType type, ScriptKey key, string script) {
|
|
RegisterClientCall(type, key, script);
|
|
}
|
|
|
|
internal void RegisterOnSubmitStatement(ScriptKey key, string script) {
|
|
RegisterClientCall(ClientAPIRegisterType.OnSubmitStatement, key, script);
|
|
}
|
|
|
|
internal void RegisterArrayDeclaration(string arrayName, string arrayValue) {
|
|
RegisterClientCall(ClientAPIRegisterType.ArrayDeclaration,
|
|
arrayName, arrayValue);
|
|
}
|
|
|
|
internal void RegisterHiddenField(string hiddenFieldName, string hiddenFieldInitialValue) {
|
|
RegisterClientCall(ClientAPIRegisterType.HiddenField,
|
|
hiddenFieldName, hiddenFieldInitialValue);
|
|
}
|
|
|
|
internal void RegisterExpandoAttribute(string controlID, string attributeName, string attributeValue) {
|
|
RegisterClientCall(ClientAPIRegisterType.ExpandoAttribute, controlID, attributeName, attributeValue);
|
|
}
|
|
|
|
internal void RegisterForEventValidation(string uniqueID, string argument) {
|
|
RegisterClientCall(ClientAPIRegisterType.EventValidation, uniqueID, argument);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public class StaticPartialCachingControl : BasePartialCachingControl {
|
|
|
|
private BuildMethod _buildMethod;
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public StaticPartialCachingControl(string ctrlID, string guid, int duration,
|
|
string varyByParams, string varyByControls, string varyByCustom,
|
|
BuildMethod buildMethod)
|
|
:this(ctrlID, guid, duration, varyByParams, varyByControls,
|
|
varyByCustom, null, buildMethod, null)
|
|
{
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public StaticPartialCachingControl(string ctrlID, string guid, int duration,
|
|
string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
|
|
BuildMethod buildMethod)
|
|
:this(ctrlID, guid, duration, varyByParams, varyByControls,
|
|
varyByCustom, sqlDependency, buildMethod, null)
|
|
{
|
|
}
|
|
|
|
public StaticPartialCachingControl(string ctrlID, string guid, int duration,
|
|
string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
|
|
BuildMethod buildMethod, string providerName) {
|
|
_ctrlID = ctrlID;
|
|
Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, duration /*seconds*/);
|
|
|
|
SetVaryByParamsCollectionFromString(varyByParams);
|
|
|
|
if (varyByControls != null)
|
|
_varyByControlsCollection = varyByControls.Split(varySeparator);
|
|
_varyByCustom = varyByCustom;
|
|
_guid = guid;
|
|
_buildMethod = buildMethod;
|
|
_sqlDependency = sqlDependency;
|
|
_provider = providerName;
|
|
}
|
|
|
|
internal override Control CreateCachedControl() {
|
|
return _buildMethod();
|
|
}
|
|
|
|
/*
|
|
* Called by generated code (hence must be public).
|
|
* Create a StaticPartialCachingControl and add it as a child
|
|
*/
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
static public void BuildCachedControl(Control parent, string ctrlID, string guid,
|
|
int duration, string varyByParams, string varyByControls, string varyByCustom,
|
|
BuildMethod buildMethod) {
|
|
BuildCachedControl(parent, ctrlID, guid, duration, varyByParams,
|
|
varyByControls, varyByCustom, null, buildMethod, null);
|
|
}
|
|
|
|
|
|
/// <internalonly/>
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
static public void BuildCachedControl(Control parent, string ctrlID, string guid,
|
|
int duration, string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
|
|
BuildMethod buildMethod) {
|
|
BuildCachedControl(parent, ctrlID, guid, duration, varyByParams,
|
|
varyByControls, varyByCustom, sqlDependency, buildMethod, null);
|
|
}
|
|
|
|
static public void BuildCachedControl(Control parent, string ctrlID, string guid,
|
|
int duration, string varyByParams, string varyByControls, string varyByCustom, string sqlDependency,
|
|
BuildMethod buildMethod, string providerName) {
|
|
|
|
StaticPartialCachingControl pcc = new StaticPartialCachingControl(
|
|
ctrlID, guid, duration, varyByParams, varyByControls, varyByCustom, sqlDependency,
|
|
buildMethod, providerName);
|
|
|
|
((IParserAccessor)parent).AddParsedSubObject(pcc);
|
|
}
|
|
}
|
|
|
|
|
|
/// <devdoc>
|
|
/// <para>[To be supplied.]</para>
|
|
/// </devdoc>
|
|
public class PartialCachingControl : BasePartialCachingControl {
|
|
|
|
private IWebObjectFactory _objectFactory;
|
|
private Type _createCachedControlType;
|
|
private object[] _args;
|
|
|
|
|
|
public Control CachedControl { get { return _cachedCtrl; } }
|
|
|
|
internal PartialCachingControl(IWebObjectFactory objectFactory, Type createCachedControlType,
|
|
PartialCachingAttribute cacheAttrib, string cacheKey, object[] args) {
|
|
string providerName = cacheAttrib.ProviderName;
|
|
_ctrlID = cacheKey;
|
|
Duration = new TimeSpan(0 /*hours*/, 0 /*mins*/, cacheAttrib.Duration /*seconds*/);
|
|
|
|
SetVaryByParamsCollectionFromString(cacheAttrib.VaryByParams);
|
|
|
|
if (cacheAttrib.VaryByControls != null)
|
|
_varyByControlsCollection = cacheAttrib.VaryByControls.Split(varySeparator);
|
|
_varyByCustom = cacheAttrib.VaryByCustom;
|
|
_sqlDependency = cacheAttrib.SqlDependency;
|
|
if (providerName == OutputCache.ASPNET_INTERNAL_PROVIDER_NAME) {
|
|
providerName = null;
|
|
}
|
|
_provider = providerName;
|
|
_guid = cacheKey;
|
|
_objectFactory = objectFactory;
|
|
_createCachedControlType = createCachedControlType;
|
|
_args = args;
|
|
}
|
|
|
|
internal override Control CreateCachedControl() {
|
|
|
|
Control cachedControl;
|
|
|
|
if (_objectFactory != null) {
|
|
cachedControl = (Control) _objectFactory.CreateInstance();
|
|
}
|
|
else {
|
|
// Instantiate the control
|
|
cachedControl = (Control) HttpRuntime.CreatePublicInstance(_createCachedControlType, _args);
|
|
}
|
|
|
|
// If it's a user control, do some extra initialization
|
|
UserControl uc = cachedControl as UserControl;
|
|
if (uc != null)
|
|
uc.InitializeAsUserControl(Page);
|
|
|
|
cachedControl.ID = _ctrlID;
|
|
|
|
return cachedControl;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Holds param names that this cached item varies by.
|
|
*/
|
|
[Serializable]
|
|
internal class ControlCachedVary {
|
|
private Guid _cachedVaryId;
|
|
internal readonly string[] _varyByParams;
|
|
internal readonly string _varyByCustom;
|
|
internal readonly string[] _varyByControls;
|
|
|
|
internal Guid CachedVaryId { get { return _cachedVaryId; } }
|
|
|
|
internal ControlCachedVary(string[] varyByParams,
|
|
string[] varyByControls, string varyByCustom) {
|
|
_varyByParams = varyByParams;
|
|
_varyByControls = varyByControls;
|
|
_varyByCustom = varyByCustom;
|
|
_cachedVaryId = Guid.NewGuid();
|
|
}
|
|
|
|
public override bool Equals (Object obj) {
|
|
|
|
if (!(obj is ControlCachedVary))
|
|
return false;
|
|
|
|
ControlCachedVary cv = (ControlCachedVary) obj;
|
|
|
|
return _varyByCustom == cv._varyByCustom &&
|
|
StringUtil.StringArrayEquals(_varyByParams, cv._varyByParams) &&
|
|
StringUtil.StringArrayEquals(_varyByControls, cv._varyByControls);
|
|
}
|
|
|
|
public override int GetHashCode () {
|
|
HashCodeCombiner hashCodeCombiner = new HashCodeCombiner();
|
|
|
|
// We need non-randomized hash code for _varyByCustom
|
|
hashCodeCombiner.AddInt(StringUtil.GetNonRandomizedHashCode(_varyByCustom));
|
|
|
|
hashCodeCombiner.AddArray(_varyByParams);
|
|
hashCodeCombiner.AddArray(_varyByControls);
|
|
return hashCodeCombiner.CombinedHash32;
|
|
}
|
|
}
|
|
|
|
}
|
|
|