Pull to refresh

App.Config и Custom Configuration Sections

Reading time5 min
Views124K
Данная статья может показаться банальной, но иногда что-то полезное забывается, а читать на импортном языке лень и книжки под рукой нет. Поэтому я, обратившись к гуглу, нашел хорошее описание процесса создания обработчика конфигурационной секции файла app.config в .net приложениях, перевел его, дополнил замечаниями (курсив) и комментариями и решил опубликовать.

Я уверен, что многим из вас приходилось использовать файл конфигурации App.Config для хранения инициализирующих или конфигурационных данных приложения. И я так же уверен в том, что многим из вас хотелось создать в данном файле свои собственные структуры для хранения настроек. Но в итоге приходилось использовать встроенные возможности секции <appSettings> и получать значения, используя конструкцию вида:


ConfigurationManager.AppSettings["MyKey"]


Что ж, я давно хотел выяснить, как использовать возможности класса ConfigurationSection для описания и загрузки данных, определенных в моем собственном формате. После нескольких часов экспериментов и гугления я смог создать свою структуру данных в файле конфигурации и воспользоваться ей в своем приложении.

Итак, для того что бы загрузить свою структуру данных из файла App.Config нам потребуются следующие классы:
  1. ConfigurationSection — Этот объект вернет нам пользовательскую секцию.
  2. ConfigurationElementCollection — Это собственно коллекция элементов, которые мы определим в пользовательской секции.
  3. ConfigurationElement — Это сам элемент, описывающий какую-от определенную вами сущность.



Первое, что нам потребуется сделать, это добавить в наше приложение файл app.Config (если, конечно, вы этого еще не сделали). После чего открываем данный файл и копипастим следующий код между тегами <configuration>:


<configSections>
<!-- 
name = Имя, которое используется для ссылки на данный раздел в файле настройки.
type = Обработчик раздела настроек. Включает две секции: полный путь - пространство имен обработчика наших данных + имя самого обработчика, наименование сборки, где данный класс располагается.
-->
    <section name="StartupFolders" type="ConfigSectionTester.StartupFoldersConfigSection, ConfigSectionTester"/>
</configSections>


Замечание: данная секция должна располагаться в самом начале файла конфигурации, т.е. сразу после тега <configuration>, иначе будут ошибки инициализации конфигурации.

Далее создадим нашу собственную секцию, которая будет реализовывать нашу собственную модель данных:


<StartupFolders>
    <Folders>
        <add folderType="A" path="c:\foo" />
        <add folderType="B" path="C:\foo1" />
    </Folders>
</StartupFolders>


Замечание: если кому-то не нравиться добавление узлов командой add в данном примере, то всегда можно сделать свой собственный префикс, используя следующий код:

[ConfigurationCollection( typeof( FolderElement ) ), AddItemName = "Folder"]
public class FoldersCollection : ConfigurationElementCollection
{

при определении коллекции элементов в структуре данных. Тогда в конфигурационном файле можно будет писать так:

<StartupFolders>
   <Folders>
     <Folder folderType="A" path="c:\foo" />
     <Folder folderType="B" path="C:\foo1" />
   </Folders>
 </StartupFolders>



Закончим модификации в файле конфигурации и перейдем к организации взаимодействия нашего приложения с ним.

Первым делом создадим класс-наследник от ConfigurationSection, что позволит нам взаимодействовать с нашей секцией в файле конфигурации через ConfigurationManager во время исполнения программы.


public class StartupFoldersConfigSection : ConfigurationSection
{
    [ConfigurationProperty( "Folders" )]
    public FoldersCollection FolderItems
   {
        get { return ( (FoldersCollection)( base[ "Folders" ] ) ); }
    }
}

Атрибут ConfigurationProperty( «Folders» ) требуется для сопоставления свойства FolderItems с корневым узлом нашей структуры данных.

Класс FoldersCollection является наследником ConfigurationElementCollection, который обеспечивает взаимодействие с коллекцией наших элементов, описанных в app.config. Определяется класс так:


[ConfigurationCollection( typeof( FolderElement ) )]
public class FoldersCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new FolderElement();
    }

    protected override object GetElementKey( ConfigurationElement element )
    {
        return ( (FolderElement)( element ) ).FolderType;
    }

    public FolderElement this[int idx ]
    {
        get{return (FolderElement) BaseGet(idx); }
    }
}


Последним нам нужно создать ConfigurationElement, класс который свяжет нас с конечными данными, определенными в конфигурационном файле.


public class FolderElement : ConfigurationElement
{

    [ConfigurationProperty("folderType", DefaultValue="", IsKey=true, IsRequired=true)]
    public string FolderType
    {
        get {return ((string) (base["folderType"]));}
        set{base["folderType"] = value; }
    }

    [ConfigurationProperty( "path", DefaultValue = "", IsKey = false, IsRequired = false )]
    public string Path
    {
        get{return ( (string)( base[ "path" ] ) ); }
        set{base[ "path" ] = value; }
    }
}


Атрибут ConfigurationProperty(«folderType») требуется для того, что бы проассоциировать имя xml-атрибута в файле конфигурации. Остальные параметры атрибута такие как DefaultValue="", IsKey=true, IsRequired=true определяют только различные опции применимые к свойствам.

Замечание: автор умалчивает, что при стандартном способе использования сеттер свойства FolderType, работать не будет, т.к. файл конфигурации обычно доступен только на чтение. Для того что бы представлялось возможным производить запись в файл конфигурации следует делать, например, так:


Configuration cfg = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
StartupFoldersConfigSection section = (StartupFoldersConfigSection)cfg.Section["StartupFolders"];
if ( section != null )
{
    System.Diagnostics.Debug.WriteLine( section.FolderItems[0].FolderType );
    System.Diagnostics.Debug.WriteLine( section.FolderItems[0].Path );
    section.FolderItems[0].Path = "C:\\Nanook";
    cfg.Save(); //устанавливает перенос на новую строку и производит проверку <exename>.vshost.exe.config файла в вашей отладочной папке.
}


Итак, мы имеем все необходимые данные и классы, которые предоставляют нам возможность хранить в конфигурационном файле app.config пользовательскую структуру данных.
Использовать данный подход можно так:


StartupFoldersConfigSection section = (StartupFoldersConfigSection)ConfigurationManager.GetSection( "StartupFolders" );

if ( section != null )
{
System.Diagnostics.Debug.WriteLine( section.FolderItems[ 0 ].FolderType );
System.Diagnostics.Debug.WriteLine( section.FolderItems[ 0 ].Path );
}

При этом не забываем прописать System.Configuration в список подключаемых пространств имен.

Автор: Derik Whittaker
Tags:
Hubs:
+21
Comments15

Articles