Benutzerdefinierter Abschnitt app.config mit einer einfachen Liste von Elementen zum Hinzufügen

88

Wie erstelle ich einen benutzerdefinierten Abschnitt app.config, der nur eine einfache Liste von addElementen enthält?

Ich habe einige Beispiele (z. B. Erstellen eines benutzerdefinierten Konfigurationsabschnitts in app.config? ) Für benutzerdefinierte Abschnitte gefunden, die folgendermaßen aussehen:

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

Aber wie vermeide ich das zusätzliche Sammlungselement ("Unternehmen"), damit es genauso aussieht wie die Abschnitte appSettingsund connectionStrings? Mit anderen Worten, ich möchte:

<registerCompanies>
  <add name="Tata Motors" code="Tata"/>
  <add name="Honda Motors" code="Honda"/>
</registerCompanies>
Joe Daley
quelle
Siehe auch stackoverflow.com/questions/1779117/…
Ohad Schneider

Antworten:

112

Vollständiges Beispiel mit Code basierend auf der OP-Konfigurationsdatei:

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

Hier ist der Beispielcode zum Implementieren eines benutzerdefinierten Konfigurationsabschnitts mit reduzierter Sammlung

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

Hier ist ein Beispiel für den Zugriff auf die Konfigurationsinformationen über Code.

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
quelle
@ Jay Walker Wie gehen Sie vor, um auf das gewünschte Element zuzugreifen, dh: - config.Instances ["Tata Motors"] Ist dies möglich?
Simon
1
Sollte darauf hinweisen, dass das <configSection>direkt nach dem <configuration>Tag sein sollte, damit es funktioniert!
Vedran Kopanja
2
Sollte auch darauf hinweisen, dass <add erforderlich ist. Das Erstellen eines eigenen benutzerdefinierten <-Tags funktioniert mit dieser Antwort nicht
Steve ist ein D
8
AFAIK - Dieser Code "config [" Tata Motors "]" wird nicht kompiliert, da der Indexer von config intern geschützt ist. Sie müssen einen Weg finden, um die Elemente in der Sammlung selbst aufzulisten.
CedricB
1
@ JayWalker alles gut. Die "My.MyConfiguration, My.Assembly" in Ihrem Beispiel für den Abschnittstyp werfen mich. Ich musste nur "MyAssembly.MyConfiguration, MyAssembly" für das verwenden, was ich versuchte.
Glen
37

Kein benutzerdefinierter Konfigurationsabschnitt erforderlich.

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>

Abrufen

// 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
quelle
1
Ich denke, es beantwortet die Frage des OP nicht streng, aber ich denke, es ist eine gültige und viel einfachere Lösung. Zumindest hat es mir geholfen!
styl0r
2
@ styl0r du hast recht. es antwortet nicht streng darauf. Wenn Sie die Attribute name / code anstelle meines Lösungsschlüssels / -werts verwenden müssen, müssen Sie einen wirklich benutzerdefinierten Abschnitt verwenden. Ich gehe jedoch davon aus, dass Sie die Kontrolle über die Konfigurationsdatei haben und bessere Dinge zu tun haben, als eine benutzerdefinierte Klasse zu erstellen.
JJS
4
Sehr einfach und sauber! Es ist keine zusätzliche Bloatware für benutzerdefinierte Abschnitte / Elemente erforderlich.
Ondřej
2
Sie können auch auf Version = 4.0.0.0 aktualisieren, wenn Sie möchten, indem Sie einfach die Versionsnummer ändern. Dies ist die beste Antwort imo, wenn Sie nur zusätzliche einfache Listen benötigen. Dasselbe kann auch für "System.Configuration.ConnectionStringsSection" durchgeführt werden, obwohl Duplikate geringfügig anders behandelt werden als die App-Einstellungen.
Sharpiro
@ Sharpiro hatten Sie Probleme mit der Assembly-Version? Ich dachte, die Assembly-Bindung wäre selbst für neuere Versionen des Frameworks im richtigen Tempo gewesen.
JJS
21

Basierend auf der obigen Antwort von Jay Walker ist dies ein vollständiges Arbeitsbeispiel, das die Möglichkeit zur Indizierung bietet:

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

Hier ist der Beispielcode zum Implementieren eines benutzerdefinierten Konfigurationsabschnitts mit reduzierter Sammlung

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

Hier ist ein Beispiel für den Zugriff auf die Konfigurationsinformationen über Code.

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
quelle
2
Das ist toll. Jetzt brauchen wir nur noch Beispielcode zum Aktualisieren, Hinzufügen und Löschen einer Instanz.
Scott Hutchinson
1
Vielen Dank für Ihre Lösung! Wer auch immer das bei MS gemacht hat ... das ist wirklich unnötig kompliziert.
Switch386
8

Basierend auf der Antwort von Jay Walker muss der Zugriff auf die Elemente durch Durchlaufen der Sammlung "Instanzen" erfolgen. dh.

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

foreach (MyConfigInstanceElement e in config.Instances) { 
   Console.WriteLine("Name: {0}, Code: {1}", e.Name, e.Code); 
}
Bonneech
quelle