Niestandardowa sekcja app.config z prostą listą elementów „dodaj”

88

Jak utworzyć niestandardową sekcję app.config, która jest tylko prostą listą addelementów?

Znalazłem kilka przykładów (np. Jak utworzyć niestandardową sekcję konfiguracji w app.config? ) Dla niestandardowych sekcji, które wyglądają tak:

<RegisterCompanies>
  <Companies>
    <Company name="Tata Motors" code="Tata"/>
    <Company name="Honda Motors" code="Honda"/>
  </Companies>
</RegisterCompanies>

Ale jak uniknąć dodatkowego elementu kolekcji („Firmy”), aby wyglądał tak samo jak sekcje appSettingsi connectionStrings? Innymi słowy, chciałbym:

<registerCompanies>
  <add name="Tata Motors" code="Tata"/>
  <add name="Honda Motors" code="Honda"/>
</registerCompanies>
Joe Daley
źródło
Zobacz także stackoverflow.com/questions/1779117/…
Ohad Schneider

Odpowiedzi:

115

Pełny przykład z kodem opartym na pliku konfiguracyjnym OP:

<configuration>
    <configSections>
        <section name="registerCompanies" 
                 type="My.MyConfigSection, My.Assembly" />
    </configSections>
    <registerCompanies>
        <add name="Tata Motors" code="Tata"/>
        <add name="Honda Motors" code="Honda"/>
    </registerCompanies>
</configuration>

Oto przykładowy kod implementujący niestandardową sekcję konfiguracji ze zwiniętą kolekcją

using System.Configuration;
namespace My {
public class MyConfigSection : ConfigurationSection {
    [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
    public MyConfigInstanceCollection Instances {
        get { return (MyConfigInstanceCollection)this[""]; }
        set { this[""] = value; }
    }
}
public class MyConfigInstanceCollection : ConfigurationElementCollection {
    protected override ConfigurationElement CreateNewElement() {
        return new MyConfigInstanceElement();
    }

    protected override object GetElementKey(ConfigurationElement element) {
        //set to whatever Element Property you want to use for a key
        return ((MyConfigInstanceElement)element).Name;
    }
}

public class MyConfigInstanceElement : ConfigurationElement {
    //Make sure to set IsKey=true for property exposed as the GetElementKey above
    [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
    public string Name {
        get { return (string) base["name"]; }
        set { base["name"] = value; }
    }

    [ConfigurationProperty("code", IsRequired = true)]
    public string Code {
        get { return (string) base["code"]; }
        set { base["code"] = value; }
    } } }

Oto przykład, jak uzyskać dostęp do informacji konfiguracyjnych z kodu.

var config = ConfigurationManager.GetSection("registerCompanies") 
                 as MyConfigSection;

Console.WriteLine(config["Tata Motors"].Code);
foreach (var e in config.Instances) { 
   Console.WriteLine("Name: {0}, Code: {1}", e.Name, e.Code); 
}
Jay Walker
źródło
@Jay Walker, w jaki sposób uzyskujesz dostęp do potrzebnego elementu, tj .: - config.Instances ["Tata Motors"] czy można to zrobić?
Simon
2
Powinien wskazywać, że <configSection>powinien znajdować się tuż po <configuration>tagu, aby działał!
Vedran Kopanja,
2
Należy również zwrócić uwagę, że <add jest wymagane. Utworzenie własnego niestandardowego tagu <nie działa z tą odpowiedzią
Steve's a D
8
AFAIK - ten kod "config [" Tata Motors "]" nie skompiluje b / c indeksator konfiguracji jest chroniony wewnętrznie. będziesz musiał znaleźć sposób na samodzielne wyliczenie pozycji w kolekcji.
CedricB
1
@JayWalker wszystko dobrze. „My.MyConfiguration, My.Assembly” w Twoim przykładzie dla typu sekcji throw me. Po prostu musiałem użyć „MyAssembly.MyConfiguration, MyAssembly” do tego, co próbowałem.
Glen
38

Nie jest wymagana żadna niestandardowa sekcja konfiguracji.

App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="YourAppSettings" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </configSections>
    <!-- value attribute is optional. omit if you just want a list of 'keys' -->
    <YourAppSettings>
        <add key="one" value="1" />
        <add key="two" value="2"/>
        <add key="three" value="3"/>
        <add key="duplicate" value="aa"/>
        <add key="duplicate" value="bb"/>
    </YourAppSettings>
</configuration>

Odzyskać

// This casts to a NameValueCollection because the section is defined as a 
/// AppSettingsSection in the configSections.
NameValueCollection settingCollection = 
    (NameValueCollection)ConfigurationManager.GetSection("YourAppSettings");

var items = settingCollection.Count;
Debug.Assert(items == 4); // no duplicates... the last one wins.
Debug.Assert(settingCollection["duplicate"] == "bb");

// Just keys as per original question? done... use em.
string[] allKeys = settingCollection.AllKeys;

// maybe you did want key/value pairs. This is flexible to accommodate both.
foreach (string key in allKeys)
{
    Console.WriteLine(key + " : " + settingCollection[key]);
}
JJS
źródło
1
Wydaje mi się, że nie odpowiada to ściśle na pytanie OP, ale myślę, że jest to poprawne rozwiązanie i znacznie prostsze. Przynajmniej mi to pomogło!
styl0r
2
@ styl0r masz rację. nie odpowiada na to ściśle . Jeśli musisz użyć nazwy / kodu atrybutów zamiast klucza / wartości rozwiązania, musisz użyć prawdziwie niestandardowej sekcji. Zakładam jednak, że kontrolujesz plik konfiguracyjny i masz lepsze rzeczy do zrobienia niż tworzenie własnej klasy.
JJS,
4
Bardzo proste i czyste! Nie ma potrzeby stosowania dodatkowego oprogramowania typu bloatware dla sekcji / elementów.
Ondřej
2
Możesz również zaktualizować do wersji 4.0.0.0, jeśli chcesz, zmieniając tylko numer wersji. To najlepsza odpowiedź imo, jeśli potrzebujesz dodatkowych prostych list. To samo można zrobić dla „System.Configuration.ConnectionStringsSection”, chociaż duplikaty są obsługiwane nieco inaczej niż ustawienia aplikacji.
Sharpiro,
@Sharpiro czy miałeś problemy z wersją zestawu? Pomyślałem, że powiązanie zestawu będzie na bieżąco, nawet w przypadku nowszych wersji frameworka.
JJS,
22

W oparciu o powyższą odpowiedź Jaya Walkera , jest to kompletny przykład roboczy, który dodaje możliwość indeksowania:

<configuration>
    <configSections>
        <section name="registerCompanies" 
                 type="My.MyConfigSection, My.Assembly" />
    </configSections>
    <registerCompanies>
        <add name="Tata Motors" code="Tata"/>
        <add name="Honda Motors" code="Honda"/>
    </registerCompanies>
</configuration>

Oto przykładowy kod implementujący niestandardową sekcję konfiguracji ze zwiniętą kolekcją

using System.Configuration;
using System.Linq;
namespace My
{
   public class MyConfigSection : ConfigurationSection
   {
      [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
      public MyConfigInstanceCollection Instances
      {
         get { return (MyConfigInstanceCollection)this[""]; }
         set { this[""] = value; }
      }
   }
   public class MyConfigInstanceCollection : ConfigurationElementCollection
   {
      protected override ConfigurationElement CreateNewElement()
      {
         return new MyConfigInstanceElement();
      }

      protected override object GetElementKey(ConfigurationElement element)
      {
         //set to whatever Element Property you want to use for a key
         return ((MyConfigInstanceElement)element).Name;
      }

      public new MyConfigInstanceElement this[string elementName]
      {
         get
         {
            return this.OfType<MyConfigInstanceElement>().FirstOrDefault(item => item.Name == elementName);
         }
      }
   }

   public class MyConfigInstanceElement : ConfigurationElement
   {
      //Make sure to set IsKey=true for property exposed as the GetElementKey above
      [ConfigurationProperty("name", IsKey = true, IsRequired = true)]
      public string Name
      {
         get { return (string)base["name"]; }
         set { base["name"] = value; }
      }

      [ConfigurationProperty("code", IsRequired = true)]
      public string Code
      {
         get { return (string)base["code"]; }
         set { base["code"] = value; }
      }
   }
}

Oto przykład, jak uzyskać dostęp do informacji konfiguracyjnych z kodu.

MyConfigSection config = 
   ConfigurationManager.GetSection("registerCompanies") as MyConfigSection;

Console.WriteLine(config.Instances["Honda Motors"].Code);
foreach (MyConfigInstanceElement e in config.Instances)
{
   Console.WriteLine("Name: {0}, Code: {1}", e.Name, e.Code);
}
SwDevMan81
źródło
2
To jest świetne. Teraz potrzebujemy tylko przykładowego kodu do aktualizowania, dodawania i usuwania wystąpienia.
Scott Hutchinson
1
Dzięki za rozwiązanie! Ktokolwiek zrobił to w MS ... to jest naprawdę niepotrzebnie skomplikowane.
Switch386,
8

Zgodnie z odpowiedzią Jaya Walkera dostęp do elementów należy wykonać, przechodząc przez kolekcję „Instances”. to znaczy.

var config = ConfigurationManager.GetSection("registerCompanies") 
                 as MyConfigSection;

foreach (MyConfigInstanceElement e in config.Instances) { 
   Console.WriteLine("Name: {0}, Code: {1}", e.Name, e.Code); 
}
Bonneech
źródło