2011-08-15 12 views
9

Próbuję napisać bardzo prostą sekcję konfiguracji niestandardowej dla aplikacji .NET4. Moim celem jest to:Prosta niestandardowa sekcja konfiguracji z kolekcją w .NET4

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

Ja jednak zachować coraz ConfigurationErrorsException: „elementu Nierozpoznany«Dodaj»” gdy zgłoszę ConfigurationManager.GetSection("myServices"). Wpatrywałam się w to od jakiegoś czasu, ale nie zrozumiałam jeszcze, co robię źle. Poniżej znajduje się mój kod. To trzy klasy: ConfigSection, MyServiceSettingsCollection i MyServiceSettings.

Najpierw klasa reprezentująca całą sekcję konfiguracji. Ma bezimienną domyślną kolekcję typu MyServiceSettingsCollection. Właściwość IsDefaultCollection powinna umożliwić mi "dodanie" bezpośrednio do mojej kolekcji z elementu głównego.

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; } } 
} 

Następnie sama klasa kolekcji. Jest to typ 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; 
    } 
} 

I wreszcie klasa dla elementów w kolekcji. Na razie ta klasa ma jedną właściwość, ale będzie później (co powstrzymuje mnie przed użyciem 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; } } 
} 

Odpowiedz

11

Ok, znalazłem pozornie przypadkowy poprawkę. Zamiast tego:

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

należy użyć:

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

Nie mam pojęcia, jaka jest różnica między tymi dwoma. Dla mnie wyglądają uderzająco podobnie ... a przynajmniej nie ma żadnej sugestii, dlaczego ktoś jest preferowany w stosunku do innych.

+0

ale dobrze wiedzieć jak to działa - będzie miało coś podobnego do osiągnięcia w ciągu najbliższych tygodni, więc dzięki :-) – Yahia

+0

To dziwne. Zarówno 'IsDefaultCollection' jak i' Options' działają dla mnie. Ale posiadanie pustego łańcucha nazwy właściwości wydaje się obowiązkowe. –

+0

Patrząc na źródło referencyjne dla właściwości ConfigurationPropertyAttribute.cs, właściwość IsDefaultCollection jest tylko opakowaniem w opcji właściwości flags - http://referencesource.microsoft.com/#System.Configuration/System/Configuration/ConfigurationPropertyAttribute.cs,62 –

3

wydaje brakuje czegoś podobnego do tego

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

uzyskać więcej informacji, zobacz http://msdn.microsoft.com/en-us/library/system.configuration.configurationcollectionattribute.aspx

+0

Próbowałem już tego i nie zadziałało. Znalazłem odpowiedź, dodam ją wkrótce. –

+0

inna opcja - patrz ostatni wpis na http://www.sitepoint.com/forums/net-141/problem-custom-config-section-419546.html – Yahia

+0

Widziałem ten post na forum również. Zanim zadałem to pytanie, wykorzystałem już wszystkie swoje umiejętności Google, aby znaleźć odpowiedź :) –

3

Ponieważ spędziłem dobry czas na to, myślałem, że dodać prawdziwy przykład świat po prostu realizowane w tym popełnić: https://github.com/rhythmagency/formulate/commit/4d2a95e1a82eb6b3500ab0869b8f8b15bd3deaa9

Tu był mój cel dla mojego web.config (co udało mi do osiągnięcia):

<?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> 

jest to klasa na najwyższym poziomie „szablonów” sekcji konfiguracji:

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 

    } 

} 

Oto kolejny poziom w dół, zbieranie klasa:

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 

    } 

} 

A oto najgłębszy klasę poziomu (poszczególne szablony):

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 

    } 

} 

Ważną nieco dla mnie było mieć pusty łańcuch w ConfigurationPropertyAttribute i ustawienie IsDefaultCollection true.Nawiasem mówiąc, Kładę config w zewnętrznym pliku, który wygląda tak:

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

I w tym przypadku, moim web.config wygląda następująco:

<?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> 

Pomyślałem wspomnieć, że na wypadek, gdyby ktoś inny próbował dodać go do pliku zewnętrznego (jest to trochę nieintuicyjne, że element główny w pliku zewnętrznym jest taki sam jak element zewnętrzny z pliku web.config).

Powiązane problemy