Seção app.config personalizada com uma lista simples de elementos "adicionar"

88

Como faço para criar uma seção app.config personalizada que é apenas uma lista simples de addelementos?

Encontrei alguns exemplos (por exemplo, como criar uma seção de configuração personalizada em app.config? ) Para seções personalizadas que se parecem com isto:

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

Mas como evito o elemento de coleção extra ("Empresas") para que tenha a mesma aparência que as seções appSettingse connectionStrings? Em outras palavras, gostaria de:

<registerCompanies>
  <add name="Tata Motors" code="Tata"/>
  <add name="Honda Motors" code="Honda"/>
</registerCompanies>
Joe Daley
fonte
Consulte também stackoverflow.com/questions/1779117/…
Ohad Schneider

Respostas:

113

Exemplo completo com código baseado no arquivo de configuração 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>

Aqui está o código de amostra para implementar uma seção de configuração personalizada com coleção recolhida

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

Aqui está um exemplo de como acessar as informações de configuração do 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); 
}
Jay Walker
fonte
@Jay Walker como você acessa o item de que precisa, ou seja: - config.Instances ["Tata Motors"] é possível fazer isso?
Simon
1
Deve apontar que <configSection>deve estar logo após a <configuration>tag para que funcione!
Vedran Kopanja
2
Deve também indicar que <add é obrigatório. Criar sua própria <tag personalizada não funciona com esta resposta
Steve é ​​um D
8
AFAIK - este código "config [" Tata Motors "]" não compilará b / c o indexador de configuração está protegido internamente. você terá que encontrar uma maneira de enumerar os itens da coleção por conta própria.
CedricB
1
@JayWalker tudo de bom. O "My.MyConfiguration, My.Assembly" em seu exemplo para o tipo de seção me jogue. Eu só tive que usar "MyAssembly.MyConfiguration, MyAssembly" para o que eu estava tentando.
Glen
38

Nenhuma seção de configuração personalizada necessária.

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
fonte
1
Acho que não responde estritamente à pergunta do OP, mas acho que é uma solução válida e muito mais simples. No mínimo, isso me ajudou!
styl0r
2
@ styl0r você está certo. não responde estritamente . Se você tiver que usar os atributos nome / código em vez de minha chave / valor de solução, você terá que usar uma seção verdadeiramente personalizada. No entanto, presumo que você esteja no controle do arquivo de configuração e tenha coisas melhores a fazer do que criar uma classe personalizada.
JJS
4
Muito simples e limpo! Não há necessidade de qualquer seção customizada adicional / bloatware de elemento.
Ondřej de
2
Você também pode atualizar para a versão = 4.0.0.0 se desejar, apenas alterando o número da versão. Esta é a melhor resposta imo se você só precisar de listas simples adicionais. O mesmo pode ser feito para "System.Configuration.ConnectionStringsSection" também, embora as duplicatas sejam tratadas de maneira um pouco diferente das configurações do aplicativo.
Sharpiro
@Sharpiro você estava tendo problemas com a versão de montagem? Achei que a vinculação de assembly estaria no ritmo, mesmo para versões mais recentes da estrutura.
JJS
21

Com base na resposta de Jay Walker acima, este é um exemplo de trabalho completo que adiciona a capacidade de fazer a indexação:

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

Aqui está o código de amostra para implementar uma seção de configuração personalizada com coleção recolhida

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

Aqui está um exemplo de como acessar as informações de configuração do 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
fonte
2
Isso é ótimo. Agora precisamos apenas de um código de exemplo para atualizar, adicionar e excluir uma instância.
Scott Hutchinson
1
Obrigado pela sua solução! Quem quer que tenha feito isso na MS ... isso realmente é desnecessariamente complicado.
Switch386
8

Com base na resposta de Jay Walker, o acesso aos elementos precisa ser feito iterando por meio da coleção "Instâncias". ie.

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

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