Na vnořený element konfigurace nefunguje atribut IsRequired

Pokud v aplikaci používáte vlastní konfigurační sekce, tak jistě definujete u jednotlivých položek i to, zda-li je hodnota vyžadována nebo ne. Vlastní konfigurační sekce může vypadat v konfiguračním souboru například následovně:

<configuration>
  <configSections>
    <section name="exampleSection" type="PetrVones.Examples.ConfigurationIsRequired.ExampleConfigurationSectionHandler, ConfigurationIsRequired" />
  </configSections>
  <!-- Example section -->
  <exampleSection stringValue="text">
    <nested enabled="true" longValue="12345" />
  </exampleSection>
</configuration>


Atributem ConfigurationPropertyAttribute se označují property konfigurace, lze nastavit že hodnota dané property je vyžadována. A to pomocí ConfigurationPropertyAttribute.IsRequired = true. Pro úplnost uvádím kompletní zdrojový kód pro konfigurační sekci ukázanou výše.

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text;

namespace PetrVones.Examples.ConfigurationIsRequired
{
  /// <summary>
  /// Konfiguracni handler sekce "exampleSection"
  /// </summary>
  #region ExampleConfigurationSectionHandler

  internal sealed class ExampleConfigurationSectionHandler : ConfigurationSection
  {
    /// <summary>
    /// Jmeno konfiguracni sekce
    /// </summary>
    private const string ExampleSectionName = "exampleSection";

    private static readonly Lazy<ExampleConfigurationSectionHandler> sectionHadler = new Lazy<ExampleConfigurationSectionHandler>(() =>
      {
        ExampleConfigurationSectionHandler handler = (ExampleConfigurationSectionHandler)ConfigurationManager.GetSection(ExampleSectionName);
        if (handler == null)
          throw new ConfigurationErrorsException(String.Format("Configuration section {0} not found", ExampleSectionName));
        return handler;
      });

    /// <summary>
    /// Vraci instanci handleru konfiguracni sekce "exampleSection"
    /// </summary>
    public static ExampleConfigurationSectionHandler Section
    {
      get
      {
        return sectionHadler.Value;
      }
    }

    /// <summary>
    /// Konfiguracni hodnota "stringValue"
    /// </summary>
    [ConfigurationProperty("stringValue", IsRequired = false, DefaultValue = "Text")]
    public string StringValue
    {
      get
      {
        return (string)this["stringValue"];
      }
    }

    /// <summary>
    /// Konfiguracni element "nested"
    /// </summary>
    [ConfigurationProperty("nested", IsRequired = true)]
    public NestedElement Nested
    {
      get
      {
        return (NestedElement)this["nested"];
      }
    }
  }

  #endregion

  /// <summary>
  /// Konfiguracni element "nested"
  /// </summary>
  #region NestedElement

  internal sealed class NestedElement : ConfigurationElement
  {
    /// <summary>
    /// Konfiguracni hodnota "enabled"
    /// </summary>
    [ConfigurationProperty("enabled", IsRequired = true)]
    public bool Enabled
    {
      get
      {
        return (bool)this["enabled"];
      }
    }

    /// <summary>
    /// Konfiguracni hodnota "longValue"
    /// </summary>
    [ConfigurationProperty("longValue", IsRequired = true, DefaultValue = 1L)]
    [LongValidator(MinValue = 1)]
    public long LongValue
    {
      get
      {
        return (long)this["longValue"];
      }
    }
  }

  #endregion

}


Některé konfigurační hodnoty jsou povinné (enabled, longValue a element nested), zatímco stringValue má výchozí hodnotu "Text". Pokud například z konfiguračního souboru odstraníme atribut enabled, dostaneme vyjímku:

System.Configuration.ConfigurationErrorsException: Required attribute 'enabled' not found. (ConfigurationIsRequired.exe.Config line 7)

To je dle očekávání, protože konfigurační parametr enabled musí mít uvedenou hodnotu. Co se ale stane v případě, že odstraníme celý element nested ? Ten má také nastaveno, že je v konfiguračním souboru vyžadován. Zde kupodivu nedojde k vyjímce, ale bude vytvořen objekt NestedElement s výchozími hodnotami všech konfiguračních parametrů.

Chyba nebo vlastnost ?

Těžko zhodnotit zda-li jde o chybu nebo "vlastnost", ale spíše bych se přikláněl k tomu, že jde o chybu. Řešením je provést dodatečnou validaci všech elementů daného handleru konfigurační sekce (v našem případě ExampleConfigurationSectionHandler). To lze provést metodou DetectMissingRequiredElements, která se bude volat po vytvoření handleru konfigurační sekce:

    private static readonly Lazy<ExampleConfigurationSectionHandler> sectionHadler = new Lazy<ExampleConfigurationSectionHandler>(() =>
      {
        ExampleConfigurationSectionHandler handler = (ExampleConfigurationSectionHandler)ConfigurationManager.GetSection(ExampleSectionName);
        if (handler == null)
          throw new ConfigurationErrorsException(String.Format("Configuration section {0} not found", ExampleSectionName));
        DetectMissingRequiredElements(handler);
        return handler;
      });

    /// <summary>
    /// Detekuje chybejici vnorene elementy konfigurace.
    /// </summary>
    /// <param name="rootElement">Element konfiguracni sekce ConfigurationSection</param>
    /// <exception cref="System.Configuration.ConfigurationErrorsException"></exception>
    public static void DetectMissingRequiredElements(ConfigurationElement rootElement)
    {
      if (rootElement == null)
        throw new ArgumentNullException("rootElement");
      foreach (PropertyInformation pi in rootElement.ElementInformation.Properties)
      {
        ConfigurationElement nestedElementProperty = pi.Value as ConfigurationElement;
        if (nestedElementProperty == null)
          continue;
        if (pi.IsRequired && !nestedElementProperty.ElementInformation.IsPresent)
          throw new ConfigurationErrorsException(String.Format("Configuration element '{0}' is missing", pi.Name), pi.Source, pi.LineNumber);
        if (nestedElementProperty.ElementInformation.IsPresent)
          DetectMissingRequiredElements(nestedElementProperty);
      }
    }


Metoda DetectMissingRequiredElements rekurzivně prochází všechny elementy odvozené od ConfigurationElement. V případě že je pomocí atributu ConfigurationProperty nastaveno IsRequired na true (což je po vytvoření handleru indikováno pomocí PropertyInformation.IsRequired), kontroluje se přítomnost elementu vnořené sekce v konfiguračním souboru pomocí ElementInformation.IsPresent. Pokud není daný element přítomen v konfiguračním souboru, je vyvolána vyjímka. V tomto případě již tedy dostaneme korektně následující vyjímku:

System.Configuration.ConfigurationErrorsException: Configuration element 'nested' is missing (ConfigurationIsRequired.exe.Config line 6)

Kompletní příklad ke stažení pro Visual Studio 2010 zde: ConfigurationIsRequired.zip (4,16 kb)

Komentáře jsou uzavřeny