[C#] ConfigurationElementCollection을 사용하여 ConfigurationSection을 구현하는 방법

프로젝트에서 사용자 정의 구성 섹션을 구현하려고하는데 이해하지 못하는 예외에 대비하여 계속 실행됩니다. 누군가가 빈칸을 채울 수 있기를 바랍니다.

나는 App.config다음과 같이 보입니다 :

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

ServiceConfig다음과 같이 정의 된 요소 가 있습니다 .

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

그리고 나는 ServiceCollection다음과 같이 정의했다.

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

내가 누락 된 부분은 처리기를 위해해야 ​​할 일입니다. 원래 구현하려고 IConfigurationSectionHandler했지만 두 가지를 발견했습니다.

  1. 작동하지 않았다
  2. 더 이상 사용되지 않습니다.

config에서 내 데이터를 읽을 수 있도록 지금해야 할 일이 완전히 없어졌습니다. 도와주세요!



답변

이전 답변은 정확하지만 모든 코드를 알려 드리겠습니다.

app.config는 다음과 같아야합니다.

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

귀하 ServiceConfigServiceCollection수업은 변경되지 않습니다.

새로운 수업이 필요합니다 :

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

그리고 그 트릭을해야합니다. 그것을 소비하려면 다음을 사용할 수 있습니다.

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

ServiceConfig serviceConfig = serviceConfigSection.Services[0];


답변

다음과 같은 사용자 정의 구성 섹션을 찾고 있다면

<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.Configuration하여 프로젝트에 어셈블리 참조 추가를 시작할 수 있습니다.

내가 사용한 각각의 중첩 요소를 살펴보십시오. 첫 번째 요소는 두 가지 속성을 가진 자격 증명이므로 먼저 추가하십시오.

자격 증명 요소

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

1 차 에이전트 및 2 차 에이전트

둘 다 동일한 속성을 가지며 기본 및 장애 조치를 위해 서버 세트에 대한 주소처럼 보입니다. 따라서 다음과 같은 두 요소에 대해 하나의 요소 클래스 만 작성하면됩니다.

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

이 게시물의 뒷부분에서 하나의 클래스에 두 개의 다른 요소를 사용하는 방법에 대해 설명하겠습니다. 차이점이 없으므로 SiteId를 건너 뛰겠습니다. 하나의 속성 만 사용하여 위와 동일한 클래스를 하나만 작성하면됩니다. Lanes 컬렉션을 구현하는 방법을 알아 보겠습니다

먼저 요소 구현 클래스를 만든 다음 컬렉션 요소 클래스를 만들어야합니다.

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
    }

의 한 속성이 LanElement열거 라는 것을 알 수 있으며 열거 응용 프로그램에 정의되지 않은 구성에서 다른 값을 사용하려고하면 System.Configuration.ConfigurationErrorsException시작시 발생합니다. Ok 컬렉션 정의로 넘어갑니다

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

    }

AddItemName = "Lane"컬렉션 항목에 대해 원하는 것을 선택할 수 있도록 설정 한 것을 알 수 있습니다. 기본 항목을 “추가”하는 것을 선호하지만이 게시물을 위해서만 변경했습니다.

이제 모든 중첩 요소가 구현되었습니다. 이제 구현해야하는 클래스의 모든 요소를 ​​집계해야합니다. 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; }
        }
    }

이제 이름이 두 개인 속성이 PrimaryAgent있고 SecondaryAgent둘 다 동일한 유형을 가졌음을 알 수 있습니다. 이제이 두 요소에 대해 하나의 구현 클래스 만있는 이유를 쉽게 이해할 수 있습니다.

app.config (또는 web.config)에서 새로 발명 된이 구성 섹션을 사용하려면 응용 프로그램에 자신의 구성 섹션을 발명했음을 알리고이를 존중해야하기 때문에 다음 줄을 추가해야합니다. app.config에서 (루트 태그 시작 직후에 가능).

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

참고 : MyAssemblyName에는 .dll이 없어야합니다. 예를 들어 어셈블리 파일 이름이 myDll.dll 인 경우 myDll.dll 대신 myDll을 사용하십시오.

이 구성을 검색하려면 응용 프로그램의 어느 곳에서나 다음 코드 줄을 사용하십시오.

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

위의 게시물이 약간 복잡한 종류의 사용자 정의 구성 섹션을 시작하는 데 도움이되기를 바랍니다.

행복한 코딩 🙂

**** 편집 **** LINQ를 활성화하려면 LaneConfigCollection구현해야합니다.IEnumerable<LaneConfigElement>

그리고 다음 구현을 추가하십시오. GetEnumerator

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

수확량이 실제로 어떻게 작동하는지 아직도 혼란스러워하는 사람들을 위해이 좋은 기사를 읽으십시오

위 기사에서 취한 두 가지 핵심 사항은

실제로 메소드 실행을 종료하지는 않습니다. yield return은 메소드 실행을 일시 중지하고 다음에 메소드를 호출 할 때 (다음 열거 값에 대해) 메소드는 마지막 yield return call부터 계속 실행됩니다. 내가 생각하기에 약간 혼란스러워 … (셰이 프리드먼)

수율은 .Net 런타임의 기능이 아닙니다. C # 컴파일러에 의해 간단한 IL 코드로 컴파일되는 C # 언어 기능 일뿐입니다. (Lars Corneliussen)


답변

이것은 구성 컬렉션의 일반 코드입니다.

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

을 가지고 나면 GenericConfigurationElementCollectionconfig 섹션에서 간단하게 사용할 수 있습니다 (이것은 내 Dispatcher의 예입니다).

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

구성 요소는 구성입니다.

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

구성 파일은 다음과 같습니다.

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

그것이 도움이되기를 바랍니다!


답변

모든 구성 상용구를 수동으로 작성하지 않으려는 사람들을위한 더 쉬운 대안 …

1) NuGet에서 Nerdle.AutoConfig 설치

2) ServiceConfig 유형을 정의하십시오 (구체적인 클래스 또는 인터페이스 중 하나)

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

3) 컬렉션을 보유 할 유형이 필요합니다. 예 :

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

4) 구성 섹션을 다음과 같이 추가하십시오 (낙타 케이스 이름 지정에 유의하십시오)

<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) 자동 구성으로 매핑

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


답변

ConfigurationSection 에서 상속을 시도하십시오 . Phil Haack 의이 블로그 게시물 에 예가 있습니다.

IConfigurationSectionHandler에 대한 설명서에 따라 확인되었습니다 .

.NET Framework 버전 2.0 이상에서는 ConfigurationSection 클래스에서 파생되어 관련 구성 섹션 처리기를 구현해야합니다.


답변