Sección app.config personalizada con una lista simple de elementos "agregar"

88

¿Cómo creo una sección app.config personalizada que es solo una lista simple de addelementos?

He encontrado algunos ejemplos (por ejemplo, ¿Cómo crear una sección de configuración personalizada en app.config? ) Para secciones personalizadas que se ven así:

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

Pero, ¿cómo evito el elemento de colección adicional ("Empresas") para que tenga el mismo aspecto que las secciones appSettingsy connectionStrings? En otras palabras, me gustaría:

<registerCompanies>
  <add name="Tata Motors" code="Tata"/>
  <add name="Honda Motors" code="Honda"/>
</registerCompanies>
Joe Daley
fuente
También vea stackoverflow.com/questions/1779117/…
Ohad Schneider

Respuestas:

115

Ejemplo completo con código basado en el archivo de configuración 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>

Aquí está el código de muestra para implementar una sección de configuración personalizada con colección contraída

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

A continuación se muestra un ejemplo de cómo acceder a la información de configuración desde el código.

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); 
}
Peatón imprudente
fuente
@Jay Walker, ¿cómo se accede al elemento que necesita, es decir: - config.Instances ["Tata Motors"] es posible hacer esto?
Simon
2
¡Debería señalar que <configSection>debe estar justo después de la <configuration>etiqueta para que funcione!
Vedran Kopanja
2
También conviene señalar que se requiere <agregar. Crear su propia etiqueta <personalizada no funciona con esta respuesta
Steve es un D
8
AFAIK - este código "config [" Tata Motors "]" no se compilará porque el indexador de config está protegido internamente. tendrá que encontrar una manera de enumerar los elementos de la colección por su cuenta.
CedricB
1
@JayWalker todo bien. El "My.MyConfiguration, My.Assembly" en su ejemplo para el tipo de sección throw me. Solo tuve que usar "MyAssembly.MyConfiguration, MyAssembly" para lo que estaba intentando.
Glen
38

No se necesita una sección de configuración personalizada.

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>

Recuperar

// 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
fuente
1
Supongo que no responde estrictamente a la pregunta del OP, pero creo que es una solución válida y mucho más simple. ¡Al menos me ayudó!
styl0r
2
@ styl0r tienes razón. no la responde estrictamente . Si tiene que usar el nombre / código de los atributos en lugar de la clave / valor de mi solución, tendrá que usar una sección verdaderamente personalizada. Sin embargo, supongo que tienes el control del archivo de configuración y tienes mejores cosas que hacer que crear una clase personalizada.
JJS
4
¡Muy simple y limpio! No es necesario ningún bloatware de sección / elemento personalizado adicional.
Ondřej
2
También puede actualizar a la Versión = 4.0.0.0 si lo desea, simplemente cambiando el número de versión. Esta es la mejor respuesta en mi opinión si solo necesita listas simples adicionales. También se puede hacer lo mismo para "System.Configuration.ConnectionStringsSection", aunque los duplicados se manejan de manera ligeramente diferente a la configuración de la aplicación.
Sharpiro
@Sharpiro, ¿tenía problemas con la versión de ensamblaje? Pensé que el enlace de ensamblado habría estado en ritmo, incluso para las versiones más nuevas del marco.
JJS
22

Basado en la respuesta de Jay Walker anterior, este es un ejemplo de trabajo completo que agrega la capacidad de hacer la indexación:

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

Aquí está el código de muestra para implementar una sección de configuración personalizada con colección contraída

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

A continuación se muestra un ejemplo de cómo acceder a la información de configuración desde el código.

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
fuente
2
Esto es genial. Ahora solo necesitamos un código de ejemplo para actualizar, agregar y eliminar una instancia.
Scott Hutchinson
1
¡Gracias por tu solución! Quienquiera que haya hecho esto en MS ... esto realmente es innecesariamente complicado.
Switch386
8

Según la respuesta de Jay Walker, el acceso a los elementos debe realizarse iterando a través de la colección "Instancias". es decir.

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

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