Fichier de configuration en .NET en 10 min

Fichier de configuration simple

Ajouter un fichier à un projet avec Visual Studio

Clique droit sur le projet 
    => Add 
    => New item 
    => Application configuration file.

Le fichier rajouté sera appelé "App.config". Lorsqu’il sera copié dans le répertoire de l’exécutable, il sera nommé: "[nom de l’exécutable].exe.config".

Le contenu du fichier sera:

<?xml version="1.0" encoding="utf-8" ?>  
<configuration>  
   <appSettings>   
   </appSettings>  
</configuration> 

On peut aussi directement créer un fichier XML avec le nom "[nom de l’exécutable].exe.config" et configurer sa copie dans le dossier de l’exécutable:

Clique droit sur le fichier 
    => Properties 
    => Sélectionnez le propriété "Copy to ouput directory" 
    sur "Copy if newer".

Ajouter des paramètres dans une section prédéfinie

On peut directement ajouter des paramètres dans la section "AppSettings":

<configuration>  
   <appSettings>  
       <add key="key1" value="value1"/>  
       <add key="key2" value="value2"/>  
   </appSettings>  
</configuration> 

Les paramètres sont accessibles dans le code avec la classe System.Configuration.ConfigurationManager (dans l’assembly System.Configuration.dll):
string value1 = ConfigurationManager.AppSettings["key1"];
ou
string value2 = ConfigurationManager.AppSettings[1];

ConnectionStrings

On peut utiliser la section ConnectionStringsSection de la même façon que AppSettingsSection avec le nœud XML <connectionStrings>.

Section de configuration

ConfigurationSection

Sections de configuration pré-définies

Quelques sections prédéfinies peuvent être utilisées:

  • ProtectedConfigurationSection: propose une section de configuration cryptée.
  • IgnoreSection: section ignorée par le parser. Attention, cette section ne doit toutefois par contenir d’erreurs de syntaxe.

Section de configuration personnalisée

On peut personnaliser une section de configuration en précisant directement le type de la section personnalisée. Il faut toutefois que la section personnalisée dérive de ConfigurationSection:

public class CustomSection : ConfigurationSection 
{ 
    [ConfigurationProperty("property1", IsRequired = true)] 
    public string Property1 
    { 
        get { return (string)this["property1"]; } 
        set { this["property1"] = value; } 
    } 
 

    [ConfigurationProperty("property2", DefaultValue = "false", 
       IsRequired = false)] 
    public string Property2 
    { 
        get { return (string)this["property2"]; } 
        set { this["property2"] = value; } 
    } 
}

On définit la section personnalisée dans le fichier de configuration avec:

<configuration> 
    <configSections> 
        <section name="customSection" 
            type="ApplicationNamespace.CustomSection" /> 
    </configSections> 
    <CustomSection property1="value1" property2="false"/> 
</configuration> 

On peut lire les valeurs en utilisant le ConfigurationManager avec:

CustomSection section = (CustomSection)ConfigurationManager.GetSection(
    "customSection"); 

Attributs utilisés dans la section de configuration personnalisée
Comme on peut le voir dans l’exemple plus haut, on utilise certains attributs pour définir les propriétés des éléments de configuration: [ConfigurationProperty("property2", DefaultValue = "false", IsRequired = false)]

  • ConfigurationProperty: attribut pour indiquer une propriété de configuration de la section de configuration
  • DefaultValue: valeur par défaut
  • IsRequired: indique si la valeur est requise.
  • Description: permet de rajouter une description qui peut être exploitée au moment de l’utilisation de l’objet ConfigurationSection.

Définition des propriétés avec ConfigurationPropertyCollection
On peut définir des propriétés dans la section de configuration en les ajoutant à une liste de propriété de type ConfigurationPropertyCollection. Cette méthode permet aussi de préciser des éléments pour configurer la propriété: valeur par défaut, indiquer si la valeur est indispensable, validateur, convertisseur etc… Les propriétés sont de type ConfigurationProperty.

Par exemple:

public class TypeSafeExampleSection: ConfigurationSection 
{ 
    private static ConfigurationPropertyCollection _properties; 
    private static ConfigurationProperty _intProperty; 
 
    static TypeSafeExampleSection() 
    { 
 
        _intProperty = new ConfigurationProperty( 
            "myInt", 
            typeof(int), 
            "Infinite", 
            new InfiniteIntConverter(), 
            new IntegerValidator(-10, 10), 
            ConfigurationPropertyOptions.IsRequired 
        ); 
 
        _properties = new ConfigurationPropertyCollection(); 
        _properties.Add(_intProperty); 
    } 
 
    [ConfigurationProperty("myInt", DefaultValue="Infinite", IsRequired=true)] 
    [IntegerValidator(-10, 10)] 
    [TypeConverter(typeof(InfiniteIntConverter)] 
    public int MyInt 
    { 
        get { return (int)base[_intProperty]; } 
    } 
}

Les deux notations sont équivalentes: par attribut ou en ajoutant la propriété instanciée avec ConfigurationProperty à la collection de type ConfigurationPropertyCollection.

Validator
On définit les validateurs sous forme d’un autre attribut, par exemple:

[ConfigurationProperty("background", DefaultValue = "FFFFFF", 
    IsRequired = true)] 
[StringValidator(InvalidCharacters = "~!@#$%^&*()[]{}/;'\"|\\GHIJKLMNOPQRSTUVWXYZ", 
    MinLength = 6, MaxLength = 6)]  
public String Background { … } 

On peut utiliser d’autres validateurs:

  • CallbackValidator (CallbackValidatorAttribute): pour définir une callback particulière pour valider la valeur d’une propriété.
  • IntegerValidator IntegerValidatorAttribute), LongValidator (LongValidatorAttribute): s’applique à des entiers (sur 32 bits) ou des long (64 bits). Il permet d’indiquer des valeurs exclues (ExcludeRange), une valeur minimale (MinValue) et une valeur maximale (MaxValue).
  • TimeSpanValidator (TimeSpanValidatorAttribute): s’applique à un objet de type TimeSpan.
  • RegexStringValidator (RegexStringValidatorAttribute): pour valider une chaîne de caractères en utilisant une Regex.
  • StringValidator (StringValidatorAttribute): on peut préciser des caractères invalides (InvalidCharacters), une longueur de chaîne minimale (MinLength) ou maximale (MaxLength).
  • SubclassTypeValidator (SubclassTypeValidatorAttribute): pour effectuer une validation sur le type de la propriété, par exemple: SubclassTypeValidator(typeof(MyBaseType)).

Validator personnalisé:
On peut définir un validator en dérivant de ConfigurationValidatorBase et en surchargeant 2 méthodes:

  • CanValidate(Type type) pour indiquer si le validator est capable de valider un type donné.
  • Validate(object value): valide la valeur donnée ou lancer une ArgumentException si la validation échoue.

Par exemple, si on définit un validator de chaîne qui utilise la validation par regex:

public class RegexValidator: ConfigurationValidatorBase  
{  
    private RegexStringValidator _regexValidator;  
 
    public RegexValidator(string regex)   
    {  
        this._regexValidator = new RegexStringValidator(regex); 
    }  
 
    public override bool CanValidate(Type type)  
    {  
        return (type == typeof(string));  
    }  
 
    public override void Validate(object value)  
    {  
        this._regexValidator.Validate(value);  
    }  
} 

Cette définition suffit si on affecte un validator à une propriété avec le constructeur ConfigurationProperty. Pour utiliser la notation utilisant un attribut, il faut définir l’attribut en dérivant de ConfigurationValidatorAttribute.

Par exemple:

public class RegexValidatorAttribute: ConfigurationValidatorAttribute 
{ 
    private string _regex; 
 
    public RegexValidatorAttribute(string regex) 
    { 
        this._regex = regex; 
    } 
 
    public string Regex 
    { 
        get { return this._regex; } 
    } 
 
    public override ConfigurationValidatorBase ValidatorInstance 
    { 
        return new RegexStringWrapperValidator(this._regex); 
    } 
} 

Converter
Il est possible d’appliquer une conversion entre les valeurs lues et l’objet utilisé dans la section de configuration.

Quelques "converters" prédéfinis mais il en existe d’autres:

  • CommaDelimiterStringCollectionConverter: convertit une chaîne de caractères contenant des valeurs séparées par des virgules. La conversion se fait vers une collection de type CommaDelimiterStringCollection.
  • GenericEnumConverter: convertit une chaîne vers un type d’énumérateur.
  • InfiniteIntConverter: convertit entre une chaine et un entier comprenant la valeur infinie.
  • InfiniteTimeSpanConverter: convertit une chaine vers un TimeSpan comprenant la valeur infinie.
  • TimeSpanMinutesConverter: convertit une chaine contenant une valeur en minutes vers un TimeSpan.
  • TimeSpanMinutesOrInfiniteConverter: convertit une chaine contenant une valeur en minutes ou infini vers un TimeSpan.
  • TimeSpanSecondsConverter: convertit une chaine contenant une valeur en secondes vers un TimeSpan.
  • TimeSpanSecondsOrInfiniteConverter: convertit une chaine contenant une valeur en secondes ou infini vers un TimeSpan.
  • TypeNameConverter: convertit le nom d’un type sous forme de chaine de caractères et le type.
  • WhiteSpaceTrimStringConverter: conversion vers une chaine dont le caractère de début et la fin est supprimé s’il s’agit d’un espace (Trim).

Converter personnalisé:
On peut définir un converter personnalisé en le faisant dériver de TypeConverter:

public class CustomTypeConverter : TypeConverter 
{ 
    public override bool CanConvertFrom(ITypeDescriptorContext context, 
        Type sourceType) 
    { 
        return sourceType == typeof(string); 
    } 
 
    public override object ConvertFrom(ITypeDescriptorContext context, 
        CultureInfo culture, object value) 
    { 

        // Conversion de la valeur écrite dans le fichier de configuration 
        // vers le type de la section de configuration 
        return new CustomTypeToConvert((string)value); 
    } 
 
    public override bool CanConvertTo(ITypeDescriptorContext context, 
        Type destinationType) 
    { 
        return destinationType == typeof(string); 
    } 
 
    public override object ConvertTo(ITypeDescriptorContext context, 
        CultureInfo culture, object value, Type destinationType) 
    { 

        // Conversion de type de la section vers une chaine écrite 
        // dans le fichier de configuration 
        var val = (CustomTypeToConvert)value; 
        return val.ToString();  
    } 
} 

ConfigurationElement

Les ConfigurationElement sont des éléments personnalisables à utiliser dans les sections de configurations:

<configuration> 
  <configSections> 
    <section name="customSection" 
        type="Application.Configuration.CustomSection, Application.Configuration" /> 
  </configSections> 
 
  <customSection property1="A sample string value." property2="true"> 
    <nestedElement nestedString="1" nestedDateTime="20/11/2015"/> 
  </customSection> 
</configuration>

Le ConfigurationElement se définit de cette façon:

namespace Application.Configuration 
{ 
    public class NestedElement: ConfigurationElement 
    { 
        private static ConfigurationProperty _nestedInteger; 
        private static ConfigurationProperty _nestedDateTime; 
 
        private static ConfigurationPropertyCollection _properties; 
 
        static NestedElement() 
        { 
            _nestedInteger = new ConfigurationProperty( 
                "nestedString", 
                typeof(int), 
                0, 
                ConfigurationPropertyOptions.IsRequired 
            ); 
 
            _nestedDateTime = new ConfigurationProperty( 
                "nestedDateTime", 
                typeof(DateTime), 
                null, 
                ConfigurationPropertyOptions.IsRequired 
            ); 
 
            _properties = new ConfigurationPropertyCollection(); 
             
            _properties.Add(_nestedInteger); 
            _properties.Add(_nestedDateTime); 
        } 
          
        [ConfigurationProperty("nestedString")] 
        public int NestedInteger 
        { 
            get { return (int)base[_nestedInteger]; } 
        } 
 
        [ConfigurationProperty("nestedDateTime", IsRequired=true)] 
        public DateTime NestedDateTime 
        { 
            get { return (DateTime)base[_nestedDateTime]; } 
        } 
 
        protected override ConfigurationPropertyCollection Properties 
        { 
            get { return _properties; } 
        } 
    } 
} 

Le ConfigurationElement s’utilise dans la section de configuration de cette façon:

public class CustomSection: ConfigurationSection 
{ 
    private static ConfigurationProperty _nestedElement; 
    private static ConfigurationPropertyCollection _properties; 
 
    static CustomSection() 
    { 
        _nestedElement = new ConfigurationProperty( 
            "nestedElement", 
            typeof(NestedElement), 
            null, 
            ConfigurationPropertyOptions.IsRequired 
        ); 
 
        _properties = new ConfigurationPropertyCollection(); 
        _properties.Add(_nestedElement); 
    } 
 
    [ConfigurationProperty("nestedElement")] 
    public NestedElement Nested 
    { 
        get { return (NestedElement)base[_nestedElement]; } 
    } 
} 

ConfigurationElementCollection

Permet de définir une liste d’éléments:

<configuration> 
  <configSections> 
    <section name="customSection" 
        type="Application.Configuration.CustomSection, Application.Configuration" /> 
  </configSections> 
  <customSection> 
    <item property1="key1" property2="value1"/> 
    <item property1="key2" property2="value2"/> 
  </customSection> 
</configuration> 

La liste d’éléments se définit de la façon suivante:

namespace Application.Configuration 
{ 
    public class Element : ConfigurationElement 
    { 
        private static readonly ConfigurationPropertyCollection _properties; 
        private static readonly ConfigurationProperty _property1; 
        private static readonly ConfigurationProperty _property2; 
 
        static Element() 
        { 
            _property1 = new ConfigurationProperty("property1", typeof(string), 
                null, ConfigurationPropertyOptions.IsKey); 
            _property2 = new ConfigurationProperty("property2", typeof(string), 
                null, ConfigurationPropertyOptions.IsRequired); 
             
            _properties = new ConfigurationPropertyCollection(); 
            _properties.Add(_property1); 
            _properties.Add(_property2); 
        } 
 
        public string Property1 
        { 
            get { return (string)this["property1"]; } 
            set { this["property1"] = value; } 
        } 
 
        public string Property2 
        { 
            get { return (string)this["property2"]; } 
            set { this["property2"] = value; } 
        } 
 
        protected override ConfigurationPropertyCollection Properties 
        { 
            get { return _properties; } 
        } 
    } 
 
    public class CustomElementCollection : ConfigurationElementCollection 
    { 
        public override ConfigurationElementCollectionType CollectionType 
        { 
            get { return ConfigurationElementCollectionType.BasicMap; } 
        } 
        protected override string ElementName 
        { 
            get { return "item"; } 
        } 
 
        protected override ConfigurationPropertyCollection Properties 
        { 
            get { return new ConfigurationPropertyCollection(); } 
        } 
 
        public Element this[int index] 
        { 
            get { return (Element)BaseGet(index); } 
            set 
            { 
                if (BaseGet(index) != null) 
                { 
                    BaseRemoveAt(index); 
                } 
                base.BaseAdd(index, value); 
            } 
        } 
 
        public new Element this[string elementName] 
        { 
            get { return (Element)BaseGet(elementName); } 
        } 
 
        public void Add(Element item) 
        { 
            base.BaseAdd(item); 
        } 
 
        public void Remove(Element item) 
        { 
            BaseRemove(item); 
        } 
 
        public void RemoveAt(int index) 
        { 
            BaseRemoveAt(index); 
        } 
 
        public void Clear() 
        { 
            BaseClear(); 
        } 
 
        protected override ConfigurationElement CreateNewElement() 
        { 
            return new Element(); 
        } 
 
        protected override object GetElementKey(ConfigurationElement element) 
        { 
            if (element != null) 
                return ((Element)element).Property1; 
            else 
                return null; 
        } 
    } 

} 

La section de configuration dans laquelle on utilise la liste se définit de la façon suivante:

namespace Application.Configuration 
{ 
    public class CustomSection : ConfigurationSection 
    { 
        private static readonly ConfigurationPropertyCollection _properties; 
        private static readonly ConfigurationProperty _elements; 
 
        static CustomSection() 
        { 
            _elements = new ConfigurationProperty( 
                "",  
                typeof(CustomElementCollection),  
                null,  
                ConfigurationPropertyOptions.IsRequired 
                    | ConfigurationPropertyOptions.IsDefaultCollection 
            ); 
 
            _properties = new ConfigurationPropertyCollection(); 
            _properties.Add(_elements); 
        } 
 
        public CustomElementCollection Elements 
        { 
            get { return (CustomElementCollection)base[_elements]; } 
        } 
 
        public new Element this[string elementName] 
        { 
            get { return Elements[elementName]; } 
        } 
 
        protected override ConfigurationPropertyCollection Properties 
        { 
            get { return _properties; } 
        } 
    } 

} 

On peut atteindre la section en exécutant:

CustomSection section = (CustomSection)ConfigurationManager.GetSection(
    "customSection"); 
Element item1 = section["key1"]; 
Element item2 = section["key2"]; 

ConfigurationElementCollectionType
Ce paramètre permet de gérer le comportement de la collection d’éléments de configuration pour les "cascades de collection".

Dans une application ASP.NET, plusieurs fichiers web.config peuvent être utilisés. Les applications "enfant" vont hériter des paramètres définis dans le web.config des applications "parentes".

ConfigurationElementCollectionType permet d’indiquer comment l’héritage des éléments de configuration dans la collection seront hérités entre une application "parente" et une application "enfant":

  • AddRemoveClearMap: on peut utiliser les 3 directives "add", "remove" et "clear" dans les collections de ce type. "add" va ajouter un élément; "remove" supprime un élément si il a été ajouté dans une application "parente" et "clear" va supprimer tous les éléments hérités. Les éléments dans cette collection sont hérités dans les applications "enfant" en ajoutant les éléments des applications "parentes" en première position.

    On peut l’utiliser comme la section AppSettingsSection:

    <appSettings> 
      <add property1="key1" property2="value1" /> 
      <add property1="key2" /> 
      <clear /> 
    </appSettings>
    
  • AddRemoveClearMapAlternate: même utilisation que AddRemoveClearMap sauf que les éléments sont hérités dans les applications "enfant" en ajoutant les éléments des applications "parentes" en dernière position.
  • BasicMap: ce type est plus restrictif que AddRemoveClearMap puisque les éléments sont hérités des applications "parentes" sans pouvoir les modifier dans les applications "enfant". Les éléments dans cette collection sont hérités dans les applications "enfant" en ajoutant les éléments des applications "parentes" en première position.
  • BasicMapAlternate: même utilisation que BasicMap sauf que les éléments sont hérités dans les applications "enfant" en ajoutant les éléments des applications "parentes" en dernière position.

SectionGroup

On peut personnaliser la configuration en rajoutant des groupes de sections de configuration dans le nœud "configSections":

<configuration> 
  <configSections> 
    <sectionGroup name="customSectionGroup"> 
      <section name="firstSection" 
          type="System.Configuration.NameValueSectionHandler" /> 
    </sectionGroup> 
  </configSections> 
  <customSectionGroup> 
    <firstSection> 
      <add key="key1" value="value1"/>  
      <add key="key2" value="value2"/>  
    </firstSection>     
  </customSectionGroup> 
</configuration>

Le nom "customSectionGroup" du "sectionGroup" fait référence au nœud du même plus bas. Le type System.Configuration.NameValueSectionHandler fait référence à un type de "section handler" prédéfini.

Pour récupérer les valeurs au niveau du code:

NameValueCollection section = (NameValueCollection)ConfigurationManager.GetSection(
    "customSectionGroup/firstSection"); 

Section handler

En plus de NameValueSectionHandler utilisé plus haut, il existe d’autres "section handler" prédéfinis. On a aussi la possibilité d’implémenter un "section handler" personnalisé.

Types de "section handler" prédéfini

NameValueSectionHandler

Permet d’obtenir les valeurs sous forme de clé/valeur. Dans le code le type de collection contenant les valeurs sera NameValueCollection.

DictionarySectionHandler

De même que NameValueSectionHandler, les valeurs seront sous forme de clé/valeur mais le type de la collection dans le code sera HashTable.

SingleTagSectionHandler

Permet de définir une section dans laquelle on pourra indiquer des attributs avec n’importe quelle nom de clé:

<configuration> 
   <configSections> 
      <section name="sampleSection" 
          type="System.Configuration.SingleTagSectionHandler" /> 
   </configSections> 
   <sampleSection setting1="Value1" setting2="value two"  
                  setting3="third value" /> 
</configuration> 

De même les valeurs peuvent être récupérées dans le code sous forme de HashTable:

Hashtable section = (Hashtable)ConfigurationManager.GetSection(
    "sampleSection"); 
string value1 = section["setting1"]; 
string value2 = section["setting2"]; 
string value3 = section["customSetting"]; 

IgnoreSectionHandler

Permet de ne pas prendre en compte une section qui ne peut pas être gérée par System.Configuration. IgnoreSectionHandler n’empêche pas les exceptions dues à parsing incorrect du fichier de configuration.

Pour accéder aux paramètres de cette section, il faut "parser" directement le fichier XML.

Modification de la configuration par programmation

A l’exécution, on peut modifier et enregistrer la configuration par programmation. Par exemple:

Configuration config = ConfigurationManager.OpenExeConfiguration(
    ConfigurationUserLevel.None); 
config.AppSettings.Settings.Remove("key1"); 
config.AppSettings.Settings.Add("key1", "value1"); 
config.Save(ConfigurationSaveMode.Modified); 
ConfigurationManager.RefreshSection("appSettings"); 

ConfigurationSaveMode permet d’indiquer quelles sont les propriétés qui seront écrites:

  • Full: toutes les propriétés sont enregistrées, même si elles n’ont pas été modifiées.
  • Minimal: seules les propriétés modifiées ayant une valeur différente de la valeur précédente seront enregistrées.
  • Modified: les propriétés modifiées seront enregistrées y compris celles ayant la même valeur que précédemment.

Utilisation de fichiers externes

Sections de configuration

Il est possible de définir des sections de configuration dans des fichiers externes au fichier de configuration (i.e. le fichier nommé [nom de l’assembly].exe.config). Il suffit d’utiliser l’attribut "configSource":

Dans le fichier de configuration principal:

<configuration> 
  <configSections> 
    <section name="customSection" 
        type="Application.Configuration.CustomSection, Application.Configuration" /> 
  </configSections> 
  <customSection configSource="config/customValues.config" /> 
</configuration> 

Dans le fichier externe dans le répertoire "config":

<customSection> 
    <item property1="key1" property2="value1"/> 
    <item property1="key2" property2="value2"/> 
</customSection> 
ATTENTION:

Le fichier externe doit obligatoirement être dans le même répertoire ou dans un sous-répertoire du fichier de configuration principal pour que l’attribut "configSource" fonctionne.

Section prédéfinie "AppSettings"

Avec la section AppSettings, il est possible de définir un fichier externe en utilisant l’attribut "configSource" comme précédemment:

<configuration>  
   <connectionStrings configSource="connections.config" />  
   <appSettings configSource="appSettings.config" />  
</configuration> 

On peut aussi utiliser l’attribut "file" pour spécifier un fichier externe contenant des valeurs. L’intérêt de "file" par rapport à "configSource" est que les valeurs dans le fichier externe surcharge les valeurs définies dans le fichier principal.

Par exemple:
Dans le fichier principal:

<configuration>  
   <appSettings file="appSettings.config">  
       <add key="key1" value="valueFromMainFile"/>  
       <add key="key3" value="value3"/>  
   </appSettings>  
</configuration>

Dans “appSettings.config”:

<appSettings>  
     <add key="key1" value="valueFromExternalFile"/>  
     <add key="key2" value="value2"/> 
</appSettings> 

Dans ce cas, les valeurs dans le fichier externe surchargent celles définies dans le fichier principal. Ainsi, ConfigurationManager.AppSettings contiendra:

key="key1"/value="valueFromExternalFile" 
key="key2"/value="value2" 
key="key3"/value="value3"
Remarques:
  • Si le fichier "appSettings.config" n’existe pas, il n’y aura pas de message d’erreur et l’attribut "file" sera ignoré.
  • Il n’est pas possible de préciser un autre fichier externe à partir du fichier externe "appSettings.config" (i.e. on ne peut pas utiliser l’attribut "file" dans le fichier externe).
  • Le fichier externe doit contenir seulement un nœud "appSettings".
2 responses... add one

Bonjour,

J’ai copier exactement le code de “ConfigurationElementCollection” mais lorsque je veux récupérer mes éléments, customsection est toujours null (CustomSection section = (CustomSection)ConfigurationManager.GetSection(
“customSection”); )

Est ce qu’il y a autre chose à faire ?

Merci

Oups il y avait une erreur à la ligne:
static CustomSection()
{
_elements = new ConfigurationProperty("", typeof(CustomElementCollection), null,
ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsDefaultCollection
);

// ...
}

Il faut écrire:
_elements = new ConfigurationProperty("", typeof(CustomElementCollection), null,
ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsDefaultCollection
);

plutôt que:
_elements = new ConfigurationProperty("customSection", typeof(CustomElementCollection), null,
ConfigurationPropertyOptions.IsRequired | ConfigurationPropertyOptions.IsDefaultCollection
);

D’autre part si le customSection est toujours null à la ligne:
CustomSection section = (CustomSection)ConfigurationManager.GetSection(
“customSection”);

C’est que probablement le fichier de configuration ne se trouve pas à coté de l’exécutable.

Dans le projet, faites un clique droit sur le fichier “App.config” et cliquer sur propriétés.
Vérifier que le paramètre “Copy to output directory” est sur “Copy if newer”.

Après compilation, vérifier ensuite dans le répertoire de sortie que le fichier de configuration se trouve bien à coté de l’exécutable.
Il doit s’appeler [nom de l’exécutable].config

Faite attention ensuite aux namespaces, ils doivent être conformes aux namespaces indiqués à la ligne:

<section name="customSection" type="Application.Configuration.CustomSection, Application.Configuration" />

Leave a Reply