Cara menerapkan ConfigurationSection dengan ConfigurationElementCollection

166

Saya mencoba menerapkan bagian konfigurasi khusus dalam suatu proyek dan saya terus berlari melawan pengecualian yang tidak saya mengerti. Saya berharap seseorang dapat mengisi bagian yang kosong di sini.

Saya memiliki App.configyang terlihat seperti ini:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
        <section name="ServicesSection" type="RT.Core.Config.ServicesConfigurationSectionHandler, RT.Core"/>
    </configSections>
    <ServicesSection type="RT.Core.Config.ServicesSection, RT.Core">
            <Services>
                <AddService Port="6996" ReportType="File" />
                <AddService Port="7001" ReportType="Other" />
            </Services>
        </ServicesSection>
</configuration>

Saya memiliki ServiceConfigelemen yang didefinisikan seperti ini:

public class ServiceConfig : ConfigurationElement
  {
    public ServiceConfig() {}

    public ServiceConfig(int port, string reportType)
    {
      Port = port;
      ReportType = reportType;
    }

    [ConfigurationProperty("Port", DefaultValue = 0, IsRequired = true, IsKey = true)]
    public int Port 
    {
      get { return (int) this["Port"]; }
      set { this["Port"] = value; }
    }

    [ConfigurationProperty("ReportType", DefaultValue = "File", IsRequired = true, IsKey = false)]
    public string ReportType
    {
      get { return (string) this["ReportType"]; }
      set { this["ReportType"] = value; }
    }
  }

Dan saya memiliki ServiceCollectiondefinisi seperti:

public class ServiceCollection : ConfigurationElementCollection
  {
    public ServiceCollection()
    {
      Console.WriteLine("ServiceCollection Constructor");
    }

    public ServiceConfig this[int index]
    {
      get { return (ServiceConfig)BaseGet(index); }
      set
      {
        if (BaseGet(index) != null)
        {
          BaseRemoveAt(index);
        }
        BaseAdd(index, value);
      }
    }

    public void Add(ServiceConfig serviceConfig)
    {
      BaseAdd(serviceConfig);
    }

    public void Clear()
    {
      BaseClear();
    }

    protected override ConfigurationElement CreateNewElement()
    {
      return new ServiceConfig();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
      return ((ServiceConfig) element).Port;
    }

    public void Remove(ServiceConfig serviceConfig)
    {
      BaseRemove(serviceConfig.Port);
    }

    public void RemoveAt(int index)
    {
      BaseRemoveAt(index);
    }

    public void Remove(string name)
    {
      BaseRemove(name);
    }
  }

Bagian yang saya lewatkan adalah apa yang harus dilakukan untuk pawang. Awalnya, saya mencoba menerapkan IConfigurationSectionHandlertetapi menemukan dua hal:

  1. tidak berhasil
  2. sudah usang.

Saya benar-benar bingung sekarang tentang apa yang harus dilakukan sehingga saya dapat membaca data saya dari konfigurasi. Tolong bantu!

Chris Holmes
sumber
Saya tidak bisa mendapatkan ini berfungsi. Saya ingin melihat RT.Core.Config.ServicesSection. Saya hanya mendapatkan elemen 'AddService' yang tidak dikenal meskipun menggunakan kode dari jawaban yang diterima juga.
sirdank
Saya melewatkan ini pada awalnya juga - bagian ini: [ConfigurationCollection (typeof (ServiceCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")] AddItemName harus cocok jadi jika Anda mengubah "tambah" ke "addService" itu akan berhasil
HeatherD

Jawaban:

188

Jawaban sebelumnya benar tetapi saya akan memberikan semua kode juga.

App.config Anda akan terlihat seperti ini:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <configSections>
      <section name="ServicesSection" type="RT.Core.Config.ServiceConfigurationSection, RT.Core"/>
   </configSections>
   <ServicesSection>
      <Services>
         <add Port="6996" ReportType="File" />
         <add Port="7001" ReportType="Other" />
      </Services>
   </ServicesSection>
</configuration>

Kelas Anda ServiceConfigdan ServiceCollectiontetap tidak berubah.

Anda membutuhkan kelas baru:

public class ServiceConfigurationSection : ConfigurationSection
{
   [ConfigurationProperty("Services", IsDefaultCollection = false)]
   [ConfigurationCollection(typeof(ServiceCollection),
       AddItemName = "add",
       ClearItemsName = "clear",
       RemoveItemName = "remove")]
   public ServiceCollection Services
   {
      get
      {
         return (ServiceCollection)base["Services"];
      }
   }
}

Dan itu harus melakukan trik. Untuk mengkonsumsinya Anda dapat menggunakan:

ServiceConfigurationSection serviceConfigSection =
   ConfigurationManager.GetSection("ServicesSection") as ServiceConfigurationSection;

ServiceConfig serviceConfig = serviceConfigSection.Services[0];
Russell McClure
sumber
10
The [Add|Remove|Clear]ItemNameproperti di ConfigurationCollectionatribut tidak benar-benar diperlukan dalam kasus ini, karena "menambahkan" / "jelas" / "menghapus" sudah nama-nama default dari elemen XML.
Wim Coenen
2
Bagaimana cara membuatnya agar tag tidak ditambahkan? Tampaknya hanya berfungsi jika mereka ditambahkan. Ini tidak akan berfungsi jika itu adalah <Service Port = "6996" ReportType = "File" /> atau <Service Port = "7001" ReportType = "Other" />
JonathanWolfson
7
@JonathanWolfson: ubah saja AddItemName = "add" menjadi AddItemName = "Service"
Mubashar
Apakah ini masih pendekatan untuk .NET 4.5?
naksir
6
@ Crush: ya, tidak banyak perubahan di sudut berdebu. NET ini.
Russell McClure
84

Jika Anda mencari bagian konfigurasi khusus seperti berikut

<CustomApplicationConfig>
        <Credentials Username="itsme" Password="mypassword"/>
        <PrimaryAgent Address="10.5.64.26" Port="3560"/>
        <SecondaryAgent Address="10.5.64.7" Port="3570"/>
        <Site Id="123" />
        <Lanes>
          <Lane Id="1" PointId="north" Direction="Entry"/>
          <Lane Id="2" PointId="south" Direction="Exit"/>
        </Lanes> 
</CustomApplicationConfig>

maka Anda dapat menggunakan implementasi saya pada bagian konfigurasi sehingga untuk memulai menambahkan System.Configurationreferensi perakitan ke proyek Anda

Lihatlah setiap elemen bersarang yang saya gunakan, Yang pertama adalah Kredensial dengan dua atribut jadi mari kita tambahkan dulu

Elemen Kredensial

public class CredentialsConfigElement : System.Configuration.ConfigurationElement
    {
        [ConfigurationProperty("Username")]
        public string Username
        {
            get 
            {
                return base["Username"] as string;
            }
        }

        [ConfigurationProperty("Password")]
        public string Password
        {
            get
            {
                return base["Password"] as string;
            }
        }
    }

PrimaryAgent dan SecondaryAgent

Keduanya memiliki atribut yang sama dan tampak seperti Alamat untuk satu set server untuk primer dan failover, jadi Anda hanya perlu membuat satu elemen kelas untuk keduanya seperti mengikuti

public class ServerInfoConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("Address")]
        public string Address
        {
            get
            {
                return base["Address"] as string;
            }
        }

        [ConfigurationProperty("Port")]
        public int? Port
        {
            get
            {
                return base["Port"] as int?;
            }
        }
    }

Saya akan menjelaskan cara menggunakan dua elemen yang berbeda dengan satu kelas nanti dalam posting ini, mari kita lewati SiteId karena tidak ada perbedaan di dalamnya. Anda hanya perlu membuat satu kelas yang sama seperti di atas dengan satu properti saja. mari kita lihat bagaimana cara mengimplementasikan koleksi Lanes

itu dibagi dalam dua bagian pertama Anda harus membuat kelas implementasi elemen kemudian Anda harus membuat kelas elemen koleksi

LaneConfigElement

public class LaneConfigElement : ConfigurationElement
    {
        [ConfigurationProperty("Id")]
        public string Id
        {
            get
            {
                return base["Id"] as string;
            }
        }

        [ConfigurationProperty("PointId")]
        public string PointId
        {
            get
            {
                return base["PointId"] as string;
            }
        }

        [ConfigurationProperty("Direction")]
        public Direction? Direction
        {
            get
            {
                return base["Direction"] as Direction?;
            }
        }
    }

    public enum Direction
    { 
        Entry,
        Exit
    }

Anda dapat melihat bahwa salah satu atribut LanElementadalah Pencacahan dan jika Anda mencoba menggunakan nilai lain dalam konfigurasi yang tidak didefinisikan dalam aplikasi Pencacahan akan melempar System.Configuration.ConfigurationErrorsExceptionpada startup. Ok mari kita beralih ke Definisi Koleksi

[ConfigurationCollection(typeof(LaneConfigElement), AddItemName = "Lane", CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class LaneConfigCollection : ConfigurationElementCollection
    {
        public LaneConfigElement this[int index]
        {
            get { return (LaneConfigElement)BaseGet(index); }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }

        public void Add(LaneConfigElement serviceConfig)
        {
            BaseAdd(serviceConfig);
        }

        public void Clear()
        {
            BaseClear();
        }

        protected override ConfigurationElement CreateNewElement()
        {
            return new LaneConfigElement();
        }

        protected override object GetElementKey(ConfigurationElement element)
        {
            return ((LaneConfigElement)element).Id;
        }

        public void Remove(LaneConfigElement serviceConfig)
        {
            BaseRemove(serviceConfig.Id);
        }

        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }

        public void Remove(String name)
        {
            BaseRemove(name);
        }

    }

Anda dapat melihat bahwa saya telah menetapkan AddItemName = "Lane"Anda dapat memilih apa pun yang Anda suka untuk item entri koleksi Anda, saya lebih suka menggunakan "tambahkan" yang default tetapi saya mengubahnya hanya untuk kepentingan posting ini.

Sekarang semua Elemen bersarang kami telah diimplementasikan sekarang kami harus mengumpulkan semua yang ada di kelas yang harus diimplementasikan System.Configuration.ConfigurationSection

CustomApplicationConfigSection

public class CustomApplicationConfigSection : System.Configuration.ConfigurationSection
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(CustomApplicationConfigSection));
        public const string SECTION_NAME = "CustomApplicationConfig";

        [ConfigurationProperty("Credentials")]
        public CredentialsConfigElement Credentials
        {
            get
            {
                return base["Credentials"] as CredentialsConfigElement;
            }
        }

        [ConfigurationProperty("PrimaryAgent")]
        public ServerInfoConfigElement PrimaryAgent
        {
            get
            {
                return base["PrimaryAgent"] as ServerInfoConfigElement;
            }
        }

        [ConfigurationProperty("SecondaryAgent")]
        public ServerInfoConfigElement SecondaryAgent
        {
            get
            {
                return base["SecondaryAgent"] as ServerInfoConfigElement;
            }
        }

        [ConfigurationProperty("Site")]
        public SiteConfigElement Site
        {
            get
            {
                return base["Site"] as SiteConfigElement;
            }
        }

        [ConfigurationProperty("Lanes")]
        public LaneConfigCollection Lanes
        {
            get { return base["Lanes"] as LaneConfigCollection; }
        }
    }

Sekarang Anda dapat melihat bahwa kami memiliki dua properti dengan nama PrimaryAgentdan SecondaryAgentkeduanya memiliki tipe yang sama sekarang Anda dapat dengan mudah memahami mengapa kami hanya memiliki satu kelas implementasi terhadap kedua elemen ini.

Sebelum Anda dapat menggunakan bagian konfigurasi yang baru ditemukan ini di app.config (atau web.config), Anda hanya perlu memberi tahu aplikasi bahwa Anda telah menemukan bagian konfigurasi Anda sendiri dan memberikan rasa hormat, untuk melakukannya Anda harus menambahkan baris berikut di app.config (mungkin tepat setelah awal tag root).

<configSections>
    <section name="CustomApplicationConfig" type="MyNameSpace.CustomApplicationConfigSection, MyAssemblyName" />
  </configSections>

CATATAN: MyAssemblyName harus tanpa .dll misalnya jika Anda nama file assembly adalah myDll.dll kemudian gunakan myDll bukan myDll.dll

untuk mengambil konfigurasi ini gunakan baris kode berikut di mana saja di aplikasi Anda

CustomApplicationConfigSection config = System.Configuration.ConfigurationManager.GetSection(CustomApplicationConfigSection.SECTION_NAME) as CustomApplicationConfigSection;

Saya harap posting di atas akan membantu Anda memulai dengan bagian konfigurasi kustom yang agak rumit.

Selamat Coding :)

**** Edit **** Untuk Mengaktifkan LINQ, LaneConfigCollectionAnda harus menerapkanIEnumerable<LaneConfigElement>

Dan Tambah implementasi berikut dari GetEnumerator

public new IEnumerator<LaneConfigElement> GetEnumerator()
        {
            int count = base.Count;
            for (int i = 0; i < count; i++)
            {
                yield return base.BaseGet(i) as LaneConfigElement;
            }
        }

untuk orang-orang yang masih bingung tentang bagaimana hasil benar-benar bekerja, baca artikel yang bagus ini

Dua poin utama yang diambil dari artikel di atas adalah

itu tidak benar-benar mengakhiri eksekusi metode. imbal hasil menjeda eksekusi metode dan pada saat Anda memanggilnya (untuk nilai enumerasi berikutnya), metode akan terus dieksekusi dari panggilan pengembalian imbuhan terakhir. Kedengarannya agak membingungkan saya pikir ... (Shay Friedman)

Hasil bukan fitur runtime .Net. Ini hanya fitur bahasa C # yang dikompilasi menjadi kode IL sederhana oleh kompiler C #. (Lars Corneliussen)

Mubashar
sumber
3
Terima kasih telah memberikan contoh lengkap, ini sangat membantu!
John Leidegren
46

Ini adalah kode umum untuk koleksi konfigurasi:

public class GenericConfigurationElementCollection<T> :   ConfigurationElementCollection, IEnumerable<T> where T : ConfigurationElement, new()
{
    List<T> _elements = new List<T>();

    protected override ConfigurationElement CreateNewElement()
    {
        T newElement = new T();
        _elements.Add(newElement);
        return newElement;
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return _elements.Find(e => e.Equals(element));
    }

    public new IEnumerator<T> GetEnumerator()
    {
        return _elements.GetEnumerator();
    }
}

Setelah melakukannya GenericConfigurationElementCollection, Anda dapat menggunakannya secara sederhana di bagian konfigurasi (ini adalah contoh dari Dispatcher saya):

public class  DispatcherConfigurationSection: ConfigurationSection
{
    [ConfigurationProperty("maxRetry", IsRequired = false, DefaultValue = 5)]
    public int MaxRetry
    {
        get
        {
            return (int)this["maxRetry"];
        }
        set
        {
            this["maxRetry"] = value;
        }
    }

    [ConfigurationProperty("eventsDispatches", IsRequired = true)]
    [ConfigurationCollection(typeof(EventsDispatchConfigurationElement), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")]
    public GenericConfigurationElementCollection<EventsDispatchConfigurationElement> EventsDispatches
    {
        get { return (GenericConfigurationElementCollection<EventsDispatchConfigurationElement>)this["eventsDispatches"]; }
    }
}

Elemen Config adalah config Di Sini:

public class EventsDispatchConfigurationElement : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true)]
    public string Name
    {
        get
        {
            return (string) this["name"];
        }
        set
        {
            this["name"] = value;
        }
    }
}

File konfigurasi akan terlihat seperti ini:

<?xml version="1.0" encoding="utf-8" ?>
  <dispatcherConfigurationSection>
    <eventsDispatches>
      <add name="Log" ></add>
      <add name="Notification" ></add>
      <add name="tester" ></add>
    </eventsDispatches>
  </dispatcherConfigurationSection>

Semoga ini bisa membantu!

Mzf
sumber
Keren! Sedang memikirkan hal yang sama dan menemukan bahwa saya tidak sendirian. Semoga MS mengimplementasikannya untuk semua konfigurasi FCL
abatishchev
Adakah saran tentang bagaimana melakukannya dengan BasicMap untuk Item? Saya tidak ingin menerapkan Tambah jika saya bisa menghindarinya.
SpaceCowboy74
28

Alternatif yang lebih mudah bagi mereka yang lebih suka untuk tidak menulis semua konfigurasi boilerplate secara manual ...

1) Instal Nerdle.AutoConfig dari NuGet

2) Tentukan tipe ServiceConfig Anda (baik kelas beton atau hanya antarmuka, baik akan dilakukan)

public interface IServiceConfiguration
{
    int Port { get; }
    ReportType ReportType { get; }
}

3) Anda memerlukan tipe untuk menyimpan koleksi, mis

public interface IServiceCollectionConfiguration
{
    IEnumerable<IServiceConfiguration> Services { get; } 
}

4) Tambahkan bagian konfigurasi seperti ini (perhatikan penamaan camelCase)

<configSections>
  <section name="serviceCollection" type="Nerdle.AutoConfig.Section, Nerdle.AutoConfig"/>
</configSections>

<serviceCollection>
  <services>
    <service port="6996" reportType="File" />
    <service port="7001" reportType="Other" />
  </services>
</serviceCollection>

5) Peta dengan Konfigurasi Otomatis

var services = AutoConfig.Map<IServiceCollectionConfiguration>();
planet fearofawhack
sumber
5
Terima kasih Tuhan atas jawaban ini
Svend
Untuk orang-orang yang hanya ingin menyelesaikannya dan tidak harus membuat semuanya dari awal, ini adalah jawaban yang sebenarnya :)
CodeThief
5

Cobalah mewarisi dari ConfigurationSection . Posting blog ini oleh Phil Haack memiliki contoh.

Dikonfirmasi, sesuai dokumentasi untuk IConfigurationSectionHandler :

Dalam .NET Framework versi 2.0 dan di atasnya, Anda harus berasal dari kelas ConfigurationSection untuk menerapkan penangan bagian konfigurasi yang terkait.

Jeff Ogata
sumber