it-swarm.dev

Jak zaimplementować ConfigurationSection za pomocą ConfigurationElementCollection

Próbuję zaimplementować niestandardową sekcję konfiguracji w projekcie i wciąż spotykam się z wyjątkami, których nie rozumiem. Mam nadzieję, że ktoś może tutaj wypełnić puste pola.

Mam App.config wygląda to tak:

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

Mam element ServiceConfig zdefiniowany w następujący sposób:

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

I mam ServiceCollection zdefiniowane w następujący sposób:

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

Brakuje mi tylko tego, co zrobić dla przewodnika. Początkowo próbowałem zaimplementować IConfigurationSectionHandler, ale znalazłem dwie rzeczy:

  1. to nie działało
  2. jest przestarzałe.

Jestem całkowicie zatracony w tym, co robić, więc mogę odczytać moje dane z konfiguracji. Proszę o wszelką pomoc!

156
Chris Holmes

Poprzednia odpowiedź jest poprawna, ale dam ci również cały kod.

Twój plik app.config powinien wyglądać następująco:

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

Twoje ServiceConfig i ServiceCollection klasy pozostają niezmienione.

Potrzebujesz nowej klasy:

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

I to powinno wystarczyć. Aby go spożyć, możesz użyć:

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

ServiceConfig serviceConfig = serviceConfigSection.Services[0];
181
Russell McClure

Jeśli szukasz niestandardowej sekcji konfiguracji, takiej jak poniżej

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

wtedy możesz użyć mojej implementacji sekcji konfiguracji, aby rozpocząć Dodaj System.Configuration Odniesienie do montażu Twojego projektu

Spójrz na każdy zagnieżdżony element, którego użyłem. Pierwszy to Poświadczenia z dwoma atrybutami, więc dodajmy go najpierw

Element referencji

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

Oba mają te same atrybuty i wydają się być adresem do zestawu serwerów dla podstawowego i przełączania awaryjnego, więc wystarczy utworzyć jedną klasę elementów dla obu tych, takich jak następujące

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

Wyjaśnię, jak używać dwóch różnych elementów z jedną klasą w dalszej części tego postu, pomińmy SiteId, ponieważ nie ma w tym różnicy. Musisz tylko utworzyć jedną klasę jak wyżej, używając tylko jednej właściwości. zobaczmy, jak zaimplementować kolekcję Lanes

jest podzielony na dwie części, najpierw musisz utworzyć klasę implementacji elementu, a następnie musisz utworzyć klasę elementu kolekcji

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
    }

możesz zauważyć, że jeden atrybut LanElement jest wyliczeniem, a jeśli spróbujesz użyć dowolnej innej wartości w konfiguracji, która nie jest zdefiniowana w aplikacji wyliczania, wyrzuci System.Configuration.ConfigurationErrorsException na starcie. Ok przejdźmy do definicji kolekcji

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

    }

możesz zauważyć, że ustawiłem AddItemName = "Lane" możesz wybrać cokolwiek zechcesz dla elementu wpisu do kolekcji, wolę użyć „dodaj” domyślnego, ale zmieniłem go tylko ze względu na ten post.

Teraz wszystkie nasze zagnieżdżone elementy zostały zaimplementowane teraz powinniśmy agregować wszystkie te w klasie, która musi zaimplementować 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; }
        }
    }

Teraz możesz zobaczyć, że mamy dwie właściwości o nazwie PrimaryAgent i SecondaryAgent oba mają ten sam typ, teraz możesz łatwo zrozumieć, dlaczego mieliśmy tylko jedną klasę implementacji dla tych dwóch elementów.

Zanim będziesz mógł użyć tej nowo wymyślonej sekcji konfiguracji w app.config (lub web.config), musisz tylko powiedzieć aplikacji, że wymyśliłeś własną sekcję konfiguracji i dać jej szacunek, aby to zrobić, musisz dodać następujące wiersze w app.config (może być zaraz po uruchomieniu tagu root).

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

UWAGA: MyAssemblyName powinna być bez .dll np. jeśli nazwa pliku zestawu to myDll.dll, użyj myDll zamiast myDll.dll

aby pobrać tę konfigurację, użyj następującego wiersza kodu w dowolnym miejscu w aplikacji

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

Mam nadzieję, że powyższy post pomoże ci zacząć od nieco skomplikowanych rodzajów niestandardowych sekcji konfiguracji.

Happy Coding :)

**** Edycja **** Aby włączyć LINQ na LaneConfigCollection musisz zaimplementować IEnumerable<LaneConfigElement>

I Dodaj następującą implementację GetEnumerator

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

dla ludzi, którzy są nadal zdezorientowani, jak naprawdę działa plon, przeczytaj ten fajny artykuł

Dwa kluczowe punkty zaczerpnięte z powyższego artykułu to

tak naprawdę to nie kończy wykonywania metody. return return wstrzymuje wykonanie metody i przy następnym wywołaniu go (dla następnej wartości wyliczenia), metoda będzie kontynuować wykonywanie od ostatniego wywołania return return. Brzmi trochę myląco Myślę, że… (Shay Friedman)

Wydajność nie jest funkcją środowiska wykonawczego .Net. Jest to po prostu funkcja języka C #, która jest kompilowana do prostego kodu IL przez kompilator C #. (Lars Corneliussen)

78
Mubashar

To jest ogólny kod do zbierania konfiguracji:

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

Po posiadaniu GenericConfigurationElementCollection, możesz po prostu użyć go w sekcji konfiguracji (jest to przykład z mojego programu rozsyłającego):

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

Element konfiguracji jest konfigurowany tutaj:

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

Plik konfiguracyjny wyglądałby tak:

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

Mam nadzieję, że to pomoże!

46
Mzf

Łatwiejsza alternatywa dla tych, którzy wolą nie pisać ręcznie całej konfiguracji płyty ...

1) Zainstaluj Nerdle.AutoConfig z NuGet

2) Zdefiniuj typ usługi ServiceConfig (konkretna klasa lub tylko interfejs, albo zrobi to)

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

3) Potrzebujesz typu do przechowywania kolekcji, np.

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

4) Dodaj sekcję konfiguracji tak (zwróć uwagę na nazewnictwo 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) Mapa z AutoConfig

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

Spróbuj dziedziczyć po ConfigurationSection . To post na blog autorstwa Phila Haacka ma przykład.

Potwierdzono, zgodnie z dokumentacją dla IConfigurationSectionHandler :

W .NET Framework w wersji 2.0 i nowszych należy zamiast tego wywodzić się z klasy ConfigurationSection, aby zaimplementować odpowiedni moduł obsługi sekcji konfiguracji.

4
Jeff Ogata