ConfigurationEectionCectionection ile bir ConfigurationSection nasıl uygulanır?


166

Bir projede özel bir yapılandırma bölümü uygulamaya çalışıyorum ve anlamadığım istisnalara karşı çalışmaya devam ediyorum. Birisinin buradaki boşlukları doldurabileceğini umuyorum.

Şöyle App.configgörünüyor:

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

Ben böyle ServiceConfigtanımlanmış bir öğe var :

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

Ve ben böyle bir ServiceCollectiontanım var :

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

Eksik olduğum kısım işleyici için ne yapılması gerektiğidir. Başlangıçta bir IConfigurationSectionHandlerşey uygulamaya çalıştım ama iki şey buldum:

  1. işe yaramadı
  2. kullanımdan kaldırıldı.

Yapılandırmadan verilerimi okuyabilmek için şimdi ne yapacağım konusunda tamamen kayboldum. Herhangi bir yardım lütfen!


Bunu çalıştıramıyorum. RT.Core.Config.ServicesSection bölümünü görmek isterim. Kabul edilen yanıttaki kodu da kullanmasına rağmen Tanınmayan öğe 'AddService' alıyorum.
sirdank

Bunu ilk başta da kaçırdım - bu bölüm: [ConfigurationCollection (typeof (ServiceCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")] "Add" öğesini değiştirdiyseniz AddItemName eşleşmelidir "addService" işe yarayacak
HeatherD

Yanıtlar:


188

Önceki cevap doğrudur ama size tüm kodu vereceğim.

App.config dosyanız şu şekilde görünmelidir:

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

Sizin ServiceConfigve ServiceCollectionsınıflarınız değişmeden kalır.

Yeni bir sınıfa ihtiyacınız var:

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

Ve bu hile yapmalı. Tüketmek için şunları kullanabilirsiniz:

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

ServiceConfig serviceConfig = serviceConfigSection.Services[0];

10
[Add|Remove|Clear]ItemNameÜzerinde özellikler ConfigurationCollection/ "add" "açık" / "kaldırma" zaten XML öğelerinin varsayılan adları çünkü özniteliği, bu durumda gerçekten gerekli değildir.
Wim Coenen

2
Etiketleri eklenmeyecek şekilde nasıl çalıştırabilirim? Sadece eklendiklerinde işe yarıyor gibi görünüyor. <Hizmet Bağlantı Noktası = "6996" olsaydı çalışmaz. ReportType = "Dosya" /> veya <Hizmet Bağlantı Noktası = "7001" ReportType = "Diğer" />
JonathanWolfson

7
@JonathanWolfson: Sadece = "Hizmet" AddItemName için AddItemName = "eklenti" olarak değiştirin
Mubashar

Bu hala .NET 4.5 için bir yaklaşım mı?
ezmek

6
@crush: evet, .NET'in bu tozlu köşesinde çok fazla değişiklik yok.
Russell McClure

84

Aşağıdaki gibi özel bir yapılandırma bölümü arıyorsanız

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

System.Configurationprojenize montaj referansı eklemek için yapılandırma bölümü uygulamamı kullanabilirsiniz .

Kullandığım her iç içe öğeye bakın, İlki iki özniteliğe sahip Kimlik Bilgileri'dir, bu yüzden önce eklemenizi sağlar

Kimlik Bilgileri Öğesi

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

Her ikisi de aynı özelliklere sahiptir ve birincil ve yük devretme için bir dizi sunucuya Adres gibi görünür, bu nedenle aşağıdakiler gibi her ikisi için de bir öğe sınıfı oluşturmanız gerekir

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

Bu yazının ilerleyen bölümlerinde bir sınıfla iki farklı öğenin nasıl kullanılacağını açıklayacağım, hiçbir fark olmadığı için SiteId'i atlayalım. Sadece tek bir özellik ile yukarıdakiyle aynı bir sınıf oluşturmanız yeterlidir. Şerit koleksiyonunun nasıl uygulanacağını görelim

önce bir öğe uygulama sınıfı oluşturmak zorunda sonra iki bölüme ayrılır sonra koleksiyon öğesi sınıfı oluşturmak zorunda

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
    }

bir özniteliğinin LanElementbir Numaralandırma olduğunu ve Numaralandırma uygulamasında tanımlanmayan yapılandırmada başka bir değer kullanmaya çalışırsanız System.Configuration.ConfigurationErrorsExceptionbaşlangıçta bir hata atacaktır . Tamam, Koleksiyon Tanımlamasına geçelim

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

    }

Eğer ben belirledik fark edebilirsiniz AddItemName = "Lane"sen i kullandığınız varsayılan bir "add" tercih koleksiyonunuzu giriş öğe için gibi ne olursa olsun sizi seçebilir ama ben sadece bu yazı uğruna değiştirdi.

Şimdi tüm iç içe Elemanlarımız uygulandı şimdi uygulamak zorunda olan bir sınıftaki tüm öğeleri toplamalıyız 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; }
        }
    }

Şimdi, isimle iki özelliğe sahip olduğumuzu PrimaryAgentve SecondaryAgenther ikisinin de aynı türe sahip olduğunu görebilirsiniz.

App.config (veya web.config) dosyasında bu yeni icat edilen yapılandırma bölümünü kullanabilmeniz için, uygulamaya kendi yapılandırma bölümünüzü icat ettiğinizi söylemeniz ve biraz saygı göstermeniz gerekir, bunun için aşağıdaki satırları eklemeniz gerekir app.config dosyasında (root etiketinin başlamasından hemen sonra olabilir).

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

NOT: Montaj dosya adı myDll.dll ise MyAssemblyName .dll olmadan olmalıdır sonra myDll.dll yerine myDll kullanın

Bu yapılandırmayı almak için uygulamanızın herhangi bir yerinde aşağıdaki kod satırını kullanın

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

Umarım yukarıdaki yazı, biraz karmaşık özel yapılandırma bölümlerine başlamanıza yardımcı olur.

Mutlu Kodlama :)

**** Edit **** Üzerinde LINQ etkinleştirmek LaneConfigCollectioniçin uygulamak zorundaIEnumerable<LaneConfigElement>

Ve aşağıdaki uygulamasını ekleyin GetEnumerator

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

hala verimin gerçekten nasıl çalıştığı konusunda kafası karışmış insanlar için bu güzel makaleyi okuyun

Yukarıdaki makaleden alınan iki kilit nokta:

yöntemin yürütülmesini gerçekten sonlandırmaz. verim dönüşü yöntem yürütmesini duraklatır ve bir sonraki çağırışınızda (sonraki numaralandırma değeri için), yöntem son verim dönüş çağrısından yürütmeye devam eder. Sanırım biraz kafa karıştırıcı geliyor… (Shay Friedman)

Getiri .Net çalışma zamanının bir özelliği değildir. C # derleyicisi tarafından basit IL koduna derlenen sadece bir C # dili özelliğidir. (Lars Corneliussen)


3
Tam bir örnek verdiğiniz için teşekkürler, bu gerçekten çok yardımcı oluyor!
John Leidegren

46

Bu, yapılandırma koleksiyonu için genel koddur:

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

Elinize geçtikten sonra GenericConfigurationElementCollection, yapılandırma bölümünde basitçe kullanabilirsiniz (bu, Dağıtım Programımdan bir örnektir):

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

Yapılandırma Elemanı Yapılandır Burada:

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

Yapılandırma dosyası şöyle görünecektir:

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

Umarım yardımcı olur!


Güzel! Aynı şeyi düşünüyordum ve yalnız olmadığımı fark ettim. MS, tüm FCL yapılandırmaları için uygulansın
abatishchev

Öğeler için bir BasicMap ile nasıl yapılacağına dair herhangi bir öneriniz var mı? Bunu önleyebilirsem Add'i uygulamak istemiyorum.
SpaceCowboy74

28

Tüm bu yapılandırma plakasını manuel olarak yazmamayı tercih edenler için daha kolay bir alternatif ...

1) monte Nerdle.AutoConfig Nuget dan

2) ServiceConfig türünüzü tanımlayın (ya somut bir sınıf ya da sadece bir arayüz, ya da yapacak)

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

3) Koleksiyonu tutmak için bir türe ihtiyacınız olacaktır, ör.

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

4) Yapılandırma bölümünü böyle ekleyin (not camelCase adlandırması)

<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) AutoConfig ile eşleştirin

var services = AutoConfig.Map<IServiceCollectionConfiguration>();

5
Bu cevap için şükürler olsun
Svend

Sadece halletmek ve her şeyi sıfırdan oluşturmak istemeyenler için, bu gerçek cevap :)
CodeThief

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.