嵌套配置元素的 StackOverflowException

StackOverflowException for nestes configuration elements

我正在尝试使用嵌套元素进行配置。 但是父子关系是一对一的,而不是一对多的。 意思是 storageProvider 只能有一个 nestedProvider.

嵌套层数不限

...
<store storeName="123">
    <storageProvider type="disk">        
        <nestedProvider type="share">                          
             <nestedProvider type="amazon-s3">            
             </nestedProvider>       
        </nestedProvider>
    </storageProvider>
</store>
...

问题 当我使用 NestedProvider 属性 创建 StorageProviderElement 并尝试读取配置时,我在 mscorlib 中捕获了 WhosebugException。好像 .NET 中有一个错误(我使用的是 .NET 4.5)

我是做错了什么还是预期的行为?

此时我不得不将此 属性 更改为一个集合(就像您以任何其他方式所做的那样)但我仍然想知道为什么我不能嵌套一对一元素。

代码: 商店元素

public class StoreElement : ConfigurationElement
{
    private const string storeName = "storeName";
    private const string storageProvider = "storageProvider";

    [ConfigurationProperty(storeName, IsKey = true, IsRequired = true)]
    public string StoreName
    {
        get
        {
            return (string)base[storeName];
        }
    }

    [ConfigurationProperty(storageProvider, IsRequired = true)]
    public StorageProviderElement StorageProvider
    {
        get
        {
            return (StorageProviderElement)this[storageProvider];
        }
    }
}

StorageProviderElement (这是递归的)

public class StorageProviderElement : ConfigurationElement
{
    private const string type = "type";
    private const string options = "options";
    private const string nestedProvider = "nestedProvider";


    [ConfigurationProperty(type, IsRequired = true)]
    public string Type
    {
        get
        {
            return (string)base[type];
        }
    }

    [ConfigurationProperty(options, IsDefaultCollection = false, IsRequired = false)]
    public GenericConfigurationElementCollection<StorageProviderOptionElement> Options
    {
        get
        {
            return (GenericConfigurationElementCollection<StorageProviderOptionElement>) this[options];
        }
    }

    // this is what trigger stack overflow exception
    [ConfigurationProperty(nestedProvider, IsDefaultCollection = false, IsRequired = false)]
    public StorageProviderElement NestedProvider
    {
        get
        {
            return (StorageProviderElement)this[nestedProvider];
        }
    }
}

更新: 显示 WhosebugException 为何难以调试的屏幕截图。

这个异常的来源是ConfigurationElement的这个方法:

private static ConfigurationProperty CreateConfigurationPropertyFromAttributes(PropertyInfo propertyInformation)
{
  ConfigurationProperty configurationProperty = (ConfigurationProperty) null;
  if (Attribute.GetCustomAttribute((MemberInfo) propertyInformation, typeof (ConfigurationPropertyAttribute)) is ConfigurationPropertyAttribute)
    configurationProperty = new ConfigurationProperty(propertyInformation);
  if (configurationProperty != null && typeof (ConfigurationElement).IsAssignableFrom(configurationProperty.Type))
  {
    ConfigurationPropertyCollection result = (ConfigurationPropertyCollection) null;
    ConfigurationElement.PropertiesFromType(configurationProperty.Type, out result);
  }
  return configurationProperty;
}

它检查给定的 属性 上是否有 ConfigurationProperty 属性,如果有并且 属性 类型继承自 ConfigurationElement (你的情况) - 它递归地检查 属性 再次输入。如果 属性 类型与外部 class 类型相同 - 递归永远不会结束并导致 Whosebug 异常。

所以简而言之 - 你不能这样做(当你试图获取相应的部分时会立即抛出 Whosebug,而不实际调用你的任何方法):

public class StorageProviderElement : ConfigurationElement
{      
    [ConfigurationProperty("whatever")]        
    public StorageProviderElement Whatever
    {
        get;
    }
}

对我来说确实是一个错误,不确定,也许背后有一些有效的推理,但我找不到任何。

要重现的简短示例:

class Program {
    static void Main(string[] args) {
        // throws
        ConfigurationManager.GetSection("store");                        
    }
}

public class StoreElement : ConfigurationSection
{
    [ConfigurationProperty("storageProvider")]
    public StorageProviderElement StorageProvider { get; }
}

public class StorageProviderElement : ConfigurationElement {
    [ConfigurationProperty("whatever")]
    public StorageProviderElement Whatever { get; }
}

在app.config

<configSections>    
  <section name="store" type="ConsoleApp4.StoreElement, ConsoleApp4"/>
</configSections>
<store />