private void GetSectionRecursive(
string configKey, bool getLkg, bool checkPermission, bool getRuntimeObject, bool requestIsHere,
out object result, out object resultRuntimeObject) {
result = null;
resultRuntimeObject = null;
#if DBG
Debug.Assert(requestIsHere || !checkPermission, "requestIsHere || !checkPermission");
if (getLkg) {
Debug.Assert(getRuntimeObject == true, "getRuntimeObject == true");
Debug.Assert(requestIsHere == true, "requestIsHere == true");
}
#endif
//
// Store results in temporary variables, because we don't want to return
// results if an exception is thrown by CheckPermissionAllowed.
//
object tmpResult = null;
object tmpResultRuntimeObject = null;
bool requirePermission = true;
bool isResultTrustedWithoutAptca = true;
// Throw errors from initial parse, if any.
if (!getLkg) {
ThrowIfInitErrors();
}
//
// check for a cached result
//
bool hasResult = false;
SectionRecord sectionRecord = GetSectionRecord(configKey, getLkg);
if (sectionRecord != null && sectionRecord.HasResult) {
// Results should never be stored if the section has errors.
Debug.Assert(!sectionRecord.HasErrors, "!sectionRecord.HasErrors");
// Create the runtime object if requested and does not yet exist.
if (getRuntimeObject && !sectionRecord.HasResultRuntimeObject) {
try {
sectionRecord.ResultRuntimeObject = GetRuntimeObject(sectionRecord.Result);
}
catch {
//
// Ignore the error if we are attempting to retreive
// the last known good configuration.
//
if (!getLkg) {
throw;
}
}
}
// Get the cached result.
if (!getRuntimeObject || sectionRecord.HasResultRuntimeObject) {
requirePermission = sectionRecord.RequirePermission;
isResultTrustedWithoutAptca = sectionRecord.IsResultTrustedWithoutAptca;
tmpResult = sectionRecord.Result;
if (getRuntimeObject) {
tmpResultRuntimeObject = sectionRecord.ResultRuntimeObject;
}
hasResult = true;
}
}
//
// If there is no cached result, get the parent's section,
// then merge it with our own input if we have any.
//
if (!hasResult) {
FactoryRecord factoryRecord = null;
bool hasInput = (sectionRecord != null && sectionRecord.HasInput);
//
// We want to cache results in a section record if:
// - The request is made at this level, and so is likely to be
// made here again.
// OR
// - The section has input, in which case we want to
// avoid evaluating the same input multiple times.
//
bool cacheResults = (requestIsHere || hasInput);
bool isRootDeclaration;
try {
//
// We need to get a factory record to:
// - Check whether the caller has permission to access a section.
// - Determine if this is the root declaration of a config section,
// and thus the termination point for recursion.
// - Get a factory that can create a configuration section.
//
// Since most factories will be declared in machine.config and not in
// child config files, we do not optimize for checking whether a
// factory record is the root declaration, as the calculation at
// machine.config is trivial.
//
// It WILL be common in web scenarios for there to be
// deep hierarchies of config files, most of which have
// sparse input. Therefore we do not want to retreive a
// factory record if it is not necessary to do so, as
// it would always lead to an order N-squared operation,
// where N is the depth of the config hierarchy.
//
// We can skip the reteival of a factory record if:
// - This is the recursive call to GetSectionRecursive,
// AND
// - There is no section input at this level,
// AND
// - No factory is declared at this level.
//
// In this case, we'll simply continue the recursion to our parent.
//
if (requestIsHere) {
//
// Ensure that we have a valid factory record and a valid factory
// for creating sections when a request for a section is first
// made.
//
factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
//
// If initialization is delayed, complete initialization if:
// - We can't find the requested factory, and it therefore
// may be in the file we haven't yet read,
// OR
// - The definition of that factory is allowed at
// levels of the config hierarchy that have not
// been initialized.
//
// This works for client config scenarios because the default
// for AllowExeDefinition is MachineToApplication. It would not
// be useful for Web scenarios, as most sections can be requested
// Everywhere.
//
// Note that configuration errors that may be present in the
// file where intialization is delayed will be ignored, and
// thus the order in which configuration sections are requested
// will affect results. This is considered OK as it is very
// expensive to determine configuration paths to
// client user configuration files, which aren't needed by
// most applications.
//
if ( IsInitDelayed
&& ( factoryRecord == null
|| _initDelayedRoot.IsDefinitionAllowed(factoryRecord.AllowDefinition, factoryRecord.AllowExeDefinition))) {
//
// We are going to remove this record, so get any data we need
// before the reference to 'this' becomes invalid.
//
string configPath = this._configPath;
InternalConfigRoot configRoot = this._configRoot;
// Tell the host to no longer permit delayed initialization.
Host.RequireCompleteInit(_initDelayedRoot);
// Removed config at the root of where initialization is delayed.
_initDelayedRoot.Remove();
// Get the config record for this config path
BaseConfigurationRecord newRecord = (BaseConfigurationRecord) configRoot.GetConfigRecord(configPath);
// Repeat the call to GetSectionRecursive
newRecord.GetSectionRecursive(
configKey, getLkg, checkPermission,
getRuntimeObject, requestIsHere,
out result, out resultRuntimeObject);
// Return and make no more references to this record.
return;
}
//
// For compatibility with previous versions,
// return null if the section is not found
// or is a group.
//
if (factoryRecord == null || factoryRecord.IsGroup) {
return;
}
//
// Use the factory record's copy of the configKey,
// so that we don't store more than one instance
// of the same configKey.
//
configKey = factoryRecord.ConfigKey;
}
else if (hasInput) {
//
// We'll need a factory to evaluate the input.
//
factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
Debug.Assert(factoryRecord != null, "factoryRecord != null");
}
else {
//
// We don't need a factory record unless this is the root declaration.
// We know it is not the root declaration if there is no factory
// declared here. This is important to avoid a walk up the config
// hierachy when there is no input in this record.
//
factoryRecord = GetFactoryRecord(configKey, false);
if (factoryRecord == null) {
isRootDeclaration = false;
}
else {
factoryRecord = FindAndEnsureFactoryRecord(configKey, out isRootDeclaration);
Debug.Assert(factoryRecord != null, "factoryRecord != null");
}
}
// We need a factory record to check permission.
Debug.Assert(!checkPermission || factoryRecord != null, "!checkPermission || factoryRecord != null");
//
// If this is the root declaration, then we always want to cache
// the result, in order to prevent the section default from being
// created multiple times.
//
if (isRootDeclaration) {
cacheResults = true;
}
//
// We'll need a section record to cache results,
// and maybe to use in creating the section default.
//
if (sectionRecord == null && cacheResults) {
sectionRecord = EnsureSectionRecord(configKey, true);
}
//
// Retrieve the parent's runtime object if the runtimeObject
// is requested, and we are not going to merge that input
// with input in this section.
//
bool getParentRuntimeObject = (getRuntimeObject && !hasInput);
object parentResult = null;
object parentResultRuntimeObject = null;
if (isRootDeclaration) {
//
// Create the default section.
//
// Use the existing section record to create it if there is no input,
// so that the cached result is attached to the correct record.
//
SectionRecord sectionRecordForDefault = (hasInput) ? null : sectionRecord;
CreateSectionDefault(configKey, getParentRuntimeObject, factoryRecord, sectionRecordForDefault,
out parentResult, out parentResultRuntimeObject);
}
else {
//
// Get the parent section.
//
_parent.GetSectionRecursive(
configKey, false /* getLkg */, false /* checkPermission */,
getParentRuntimeObject, false /* requestIsHere */,
out parentResult, out parentResultRuntimeObject);
}
if (hasInput) {
//
// Evaluate the input.
//
// If Evaluate() encounters an error, it may not throw an exception
// when getLkg == true.
//
// The complete success of the evaluation is determined by the return value.
//
bool success = Evaluate(factoryRecord, sectionRecord, parentResult, getLkg, getRuntimeObject,
out tmpResult, out tmpResultRuntimeObject);
Debug.Assert(success || getLkg, "success || getLkg");
if (!success) {
Debug.Assert(getLkg == true, "getLkg == true");
// Do not cache partial results if getLkg was specified.
cacheResults = false;
}
}
else {
//
// If we are going to cache results here, we will need
// to create a copy in the case of MgmtConfigurationRecord -
// otherwise we could inadvertently return the parent to the user,
// which could then be modified.
//
if (sectionRecord != null) {
tmpResult = UseParentResult(configKey, parentResult, sectionRecord);
if (getRuntimeObject) {
//
// If the parent result is the same as the parent runtime object,
// then use the same copy of the parent result for our own runtime object.
//
if (object.ReferenceEquals(parentResult, parentResultRuntimeObject)) {
tmpResultRuntimeObject = tmpResult;
}
else {
tmpResultRuntimeObject = UseParentResult(configKey, parentResultRuntimeObject, sectionRecord);
}
}
}
else {
Debug.Assert(!requestIsHere, "!requestIsHere");
//
// We don't need to make a copy if we are not storing
// the result, and thus not returning the result to the
// caller of GetSection.
//
tmpResult = parentResult;
tmpResultRuntimeObject = parentResultRuntimeObject;
}
}
//
// Determine which permissions are required of the caller.
//
if (cacheResults || checkPermission) {
requirePermission = factoryRecord.RequirePermission;
isResultTrustedWithoutAptca = factoryRecord.IsFactoryTrustedWithoutAptca;
//
// Cache the results.
//
if (cacheResults) {
if (sectionRecord == null) {
sectionRecord = EnsureSectionRecord(configKey, true);
}
sectionRecord.Result = tmpResult;
if (getRuntimeObject) {
sectionRecord.ResultRuntimeObject = tmpResultRuntimeObject;
}
sectionRecord.RequirePermission = requirePermission;
sectionRecord.IsResultTrustedWithoutAptca = isResultTrustedWithoutAptca;
}
}
hasResult = true;
}
catch {
//
// Ignore the error if we are attempting to retreive
// the last known good configuration.
//
if (!getLkg) {
throw;
}
}
//
// If we don't have a result, ask our parent for its
// last known good result.
//
if (!hasResult) {
Debug.Assert(getLkg == true, "getLkg == true");
_parent.GetSectionRecursive(
configKey, true /* getLkg */, checkPermission,
true /* getRuntimeObject */, true /* requestIsHere */,
out result, out resultRuntimeObject);
return;
}
}
//
// Check if permission to access the section is allowed.
//
if (checkPermission) {
CheckPermissionAllowed(configKey, requirePermission, isResultTrustedWithoutAptca);
}
//
// Return the results.
//
result = tmpResult;
if (getRuntimeObject) {
resultRuntimeObject = tmpResultRuntimeObject;
}
}