2011-08-15 2 views
9

J'essaie d'écrire une section de configuration personnalisée très simple pour une application .NET4. Mon objectif est le suivant:Section de configuration personnalisée simple avec collection dans .NET4

<configuration> 
    <configSections> 
    <section name="myServices" type="My.ConfigSection, My.Assembly" /> 
    </configSections> 
    <myServices> 
    <add name="First" /> 
    <add name="Second" /> 
    </myServices> 
</configuration> 

Cependant, je continue à obtenir un ConfigurationErrorsException: « élément Unrecognized « ajouter » » quand je l'appelle ConfigurationManager.GetSection("myServices"). Je l'ai regardé pendant un moment maintenant mais n'ai pas encore compris ce que je fais mal. Voici mon code. Il s'agit de trois classes: ConfigSection, MyServiceSettingsCollection et MyServiceSettings.

Première classe représentant la section de configuration entière. Il a une collection par défaut sans nom de type MyServiceSettingsCollection. La propriété IsDefaultCollection devrait me permettre de 'ajouter' directement à ma collection à partir de l'élément racine.

public sealed class ConfigSection : ConfigurationSection 
{ 
    private static readonly ConfigurationProperty _propMyServices; 

    private static readonly ConfigurationPropertyCollection _properties; 

    public static ConfigSection Instance { get { return _instance; } } 

    static ConfigSection() 
    { 
    _propMyServices = new ConfigurationProperty(
      null, typeof(MyServiceSettingsCollection), null, 
      ConfigurationPropertyOptions.IsDefaultCollection); 
    _properties = new ConfigurationPropertyCollection { _propMyServices }; 
    } 

    [ConfigurationProperty("", IsDefaultCollection = true)] 
    public MyServiceSettingsCollection MyServices 
    { 
    get { return (MyServiceSettingsCollection) base[_propMyServices]; } 
    set { base[_propMyServices] = value; } 
    } 

    protected override ConfigurationPropertyCollection Properties 
    { get { return _properties; } } 
} 

Ensuite, la classe de collecte elle-même. Il est de type AddRemoveClearMap.

[ConfigurationCollection(typeof(MyServiceSettings), 
    CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap)] 
public sealed class MyServiceSettingsCollection : ConfigurationElementCollection 
{ 
    public MyServiceSettings this[int index] 
    { 
    get { return (MyServiceSettings) BaseGet(index); } 
    set 
    { 
     if (BaseGet(index) != null) { BaseRemoveAt(index); } 
     BaseAdd(index, value); 
    } 
    } 

    public new MyServiceSettings this[string key] 
    { 
    get { return (MyServiceSettings) BaseGet(key); } 
    } 

    protected override ConfigurationElement CreateNewElement() 
    { 
    return new MyServiceSettings(); 
    } 

    protected override object GetElementKey(ConfigurationElement element) 
    { 
    return ((MyServiceSettings) element).Key; 
    } 
} 

Et enfin une classe pour les éléments de la collection. Pour l'instant, cette classe a une propriété mais il y en aura plus tard (ce qui m'empêche d'utiliser NameValueSectionHandler).

public class MyServiceSettings : ConfigurationElement 
{ 
    private static readonly ConfigurationProperty _propName; 

    private static readonly ConfigurationPropertyCollection properties; 

    static MyServiceSettings() 
    { 
    _propName = new ConfigurationProperty("name", typeof(string), null, null, 
              new StringValidator(1), 
              ConfigurationPropertyOptions.IsRequired | 
              ConfigurationPropertyOptions.IsKey); 
    properties = new ConfigurationPropertyCollection { _propName }; 
    } 

    [ConfigurationProperty("name", DefaultValue = "", 
     Options = ConfigurationPropertyOptions.IsRequired | 
        ConfigurationPropertyOptions.IsKey)] 
    public string Name 
    { 
     get { return (string) base[_propKey]; } 
     set { base[_propKey] = value; } 
    } 

    protected override ConfigurationPropertyCollection Properties 
    { get { return properties; } } 
} 

Répondre

11

Ok, j'ai trouvé la solution apparemment aléatoire. Au lieu de cela:

[ConfigurationProperty("", IsDefaultCollection = true)] 
public ProvisiorServiceSettingsCollection ProvisiorServices 
{ ... } 

vous devez utiliser:

[ConfigurationProperty("", Options = ConfigurationPropertyOptions.IsDefaultCollection)] 
public ProvisiorServiceSettingsCollection ProvisiorServices 
{ ... } 

Aucune idée de ce que la différence entre les deux. Pour moi, ils ressemblent de façon frappante ... ou du moins, il n'y a aucune suggestion pourquoi l'un est préféré à l'autre.

+0

mais bon de savoir comment cela fonctionne - aura quelque chose de similaire à réaliser dans les prochaines semaines, alors merci :-) – Yahia

+0

C'est étrange. 'IsDefaultCollection' et' Options' fonctionnent tous les deux pour moi. Mais avoir une chaîne vide pour le nom de la propriété semble être obligatoire. –

+0

En regardant la source de référence pour ConfigurationPropertyAttribute.cs, la propriété IsDefaultCollection est juste un wrapper sur la propriété Options flags - http://referencesource.microsoft.com/#System.Configuration/System/Configuration/ConfigurationPropertyAttribute.cs,62 –

3

il semble que vous manquez quelque chose de semblable à ce

[ConfigurationProperty("urls", IsDefaultCollection = false)] 
    [ConfigurationCollection(typeof(UrlsCollection), 
     AddItemName = "add", 
     ClearItemsName = "clear", 
     RemoveItemName = "remove")] 

pour plus d'informations voir http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx

+0

Déjà essayé et ça n'a pas fonctionné. J'ai trouvé la réponse, je l'ajouterai bientôt. –

+0

une autre option - voir la toute dernière entrée à http://www.sitepoint.com/forums/net-141/problem-custom-config-section-419546.html – Yahia

+0

Ont vu ce message sur le forum aussi. Avant de poser cette question, j'ai déjà utilisé toutes mes compétences Google pour trouver une réponse :) –

3

Depuis que j'ai passé une bonne quantité de temps à ce sujet, je pensais ajouter un exemple du monde réel que je viens implémentées dans cette commettras: https://github.com/rhythmagency/formulate/commit/4d2a95e1a82eb6b3500ab0869b8f8b15bd3deaa9

Voici mon objectif était de mon web.config (que j'ai pu pour atteindre):

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <configSections> 
    <sectionGroup name="formulateConfiguration"> 
     <section name="templates" type="formulate.app.Configuration.TemplatesConfigSection, formulate.app" requirePermission="false"/> 
    </sectionGroup> 
    </configSections> 
    <formulateConfiguration> 
    <templates> 
     <template name="Responsive" path="~/Views/Formulate/Responsive.Bootstrap.Angular.cshtml" /> 
    </templates> 
    </formulateConfiguration> 
</configuration> 

C'est la classe le plus haut niveau "modèles" section de configuration:

namespace formulate.app.Configuration 
{ 

    // Namespaces. 
    using System.Configuration; 


    /// <summary> 
    /// A configuration section for Formulate templates. 
    /// </summary> 
    public class TemplatesConfigSection : ConfigurationSection 
    { 

     #region Properties 

     /// <summary> 
     /// The templates in this configuration section. 
     /// </summary> 
     [ConfigurationProperty("", IsDefaultCollection = true)] 
     [ConfigurationCollection(typeof(TemplateCollection), AddItemName = "template")] 
     public TemplateCollection Templates 
     { 
      get 
      { 
       return base[""] as TemplateCollection; 
      } 
     } 

     #endregion 

    } 

} 

est ici le niveau suivant, la collection Classe:

namespace formulate.app.Configuration 
{ 

    // Namespaces. 
    using System.Configuration; 


    /// <summary> 
    /// A collection of templates from the configuration. 
    /// </summary> 
    [ConfigurationCollection(typeof(TemplateElement))] 
    public class TemplateCollection : ConfigurationElementCollection 
    { 

     #region Methods 

     /// <summary> 
     /// Creates a new template element. 
     /// </summary> 
     /// <returns>The template element.</returns> 
     protected override ConfigurationElement CreateNewElement() 
     { 
      return new TemplateElement(); 
     } 


     /// <summary> 
     /// Gets the key for an element. 
     /// </summary> 
     /// <param name="element">The element.</param> 
     /// <returns>The key.</returns> 
     protected override object GetElementKey(ConfigurationElement element) 
     { 
      return (element as TemplateElement).Name; 
     } 

     #endregion 

    } 

} 

Et voici la classe la plus profonde de niveau (les modèles individuels):

namespace formulate.app.Configuration 
{ 

    // Namespaces. 
    using System.Configuration; 


    /// <summary> 
    /// A "template" configuration element. 
    /// </summary> 
    public class TemplateElement : ConfigurationElement 
    { 

     #region Constants 

     private const string DefaultPath = "~/*Replace Me*.cshtml"; 

     #endregion 


     #region Properties 

     /// <summary> 
     /// The name of the template. 
     /// </summary> 
     [ConfigurationProperty("name", IsRequired = true)] 
     public string Name 
     { 
      get 
      { 
       return base["name"] as string; 
      } 
      set 
      { 
       this["name"] = value; 
      } 
     } 


     /// <summary> 
     /// The path to this template. 
     /// </summary> 
     /// <remarks> 
     /// Should start with "~" and end with ".cshtml". 
     /// </remarks> 
     [ConfigurationProperty("path", IsRequired = true, DefaultValue = DefaultPath)] 
     [RegexStringValidator(@"^~.*\.[cC][sS][hH][tT][mM][lL]$")] 
     public string Path 
     { 
      get 
      { 
       var result = base["path"] as string; 
       return result == DefaultPath ? null : result; 
      } 
      set 
      { 
       this["path"] = value; 
      } 
     } 

     #endregion 

    } 

} 

Le bit important pour moi était d'avoir une chaîne vide dans le ConfigurationPropertyAttribute et la mise IsDefaultCollection à true.Soit dit en passant, je mets ma config dans un fichier externe qui ressemble à ceci:

<?xml version="1.0" encoding="utf-8" ?> 
<templates> 
    <template name="Responsive" path="~/Views/Formulate/Responsive.Bootstrap.Angular.cshtml" /> 
</templates> 

Et dans ce cas, mon web.config ressemble à ceci:

<?xml version="1.0" encoding="utf-8"?> 
<configuration> 
    <configSections> 
    <sectionGroup name="formulateConfiguration"> 
     <section name="templates" type="formulate.app.Configuration.TemplatesConfigSection, formulate.app" requirePermission="false"/> 
    </sectionGroup> 
    </configSections> 
    <formulateConfiguration> 
    <templates configSource="config\Formulate\templates.config"/> 
    </formulateConfiguration> 
</configuration> 

Figured Je mentionne que au cas où quelqu'un d'autre essayait de l'ajouter à un fichier externe (il est quelque peu non-intuitif que l'élément de niveau racine dans le fichier externe soit le même que l'élément externalisé du web.config).

Questions connexes