ConfigurationElementCollection을 사용하여 ConfigurationSection을 구현하는 방법


166

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

나는 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에서 내 데이터를 읽을 수 있도록 지금해야 할 일이 완전히 없어졌습니다. 도와주세요!


이 작업을 수행 할 수 없습니다. RT.Core.Config.ServicesSection을보고 싶습니다. 허용 된 답변의 코드를 사용하더라도 인식 할 수없는 요소 'AddService'가 나타납니다.
sirdank

처음에 이것을 놓쳤습니다.이 부분 : [ConfigurationCollection (typeof (ServiceCollection), AddItemName = "add", ClearItemsName = "clear", RemoveItemName = "remove")] AddItemName을 "add"로 변경 한 경우 "addService"작동합니다
HeatherD

답변:


188

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

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

10
[Add|Remove|Clear]ItemName온 속성 ConfigurationCollection/ "추가", "분명히"/ "제거"는 이미 XML 요소의 기본 이름 때문에 속성은,이 경우 정말 필요하지 않습니다.
Wim Coenen

2
태그가 추가되지 않도록 어떻게 작동합니까? 추가 된 경우에만 작동하는 것 같습니다. 그것은는 <서비스 포트 = "6996"리포트 유형 = "파일"/> 또는 <서비스 포트 = "7001"리포트 유형 = "기타"/>없는 일이었다 것인지
JonathanWolfson

7
@JonathanWolfson : AddItemName = "add"를 AddItemName = "Service"로 변경
Mubashar

이것이 여전히 .NET 4.5에 대한 접근입니까?
호감

6
@crush : 그렇습니다. .NET의 먼지가 많은 코너에서는 많은 변화가 없었습니다.
Russell McClure

84

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

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


3
완전한 예제를 제공해 주셔서 감사합니다. 이것은 정말 많은 도움이됩니다!
John Leidegren

46

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

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>

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


멋있는! 똑같은 생각을하고 있었고 나는 혼자가 아니라는 것을 알았습니다. Wish MS는 모든 FCL 구성에 대해이를 구현합니다
abatishchev

항목에 대한 BasicMap을 사용하여 수행하는 방법에 대한 제안 사항이 있습니까? 피할 수 있다면 Add를 구현하고 싶지 않습니다.
SpaceCowboy74

28

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

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

5
이 답변에 감사드립니다
Svend

단지 그것을 끝내고 모든 것을 처음부터 새로 만들 필요는없는 사람들에게는 이것이 진정한 해답입니다. :)
CodeThief

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.