В .NET Framework есть очень удобный механизм хранения настроек приложения. Как показывает практика, многие разработчики либо не понимают, как использовать потенциал этого механизма, либо вообще не знают, что в файле конфигурации можно хранить что-то большее, чем строку соединения с базой данных (в секции connectionStrings), ну или, в лучшем случае, пар ключ-значение (в секции appSettings). А механизм заключается в декларативном написании классов, которые описывают необходимые настройки.
Представим, что перед нами стоит задача импорта из текстового файла, в котором лежат данные в позиционном формате с фиксированной шириной поля. В настройках нам необходимо указать путь к файлу, кодировку файла и, конечно, описание полей. Получаем примерно такой вид:
<table fileName="files\import.txt" codePage="utf8"> <fields> <add name="Id" elementType="int" length="10" /> <add name="Name" elementType="string" length="100" /> <add name="UserName" elementType="string" length="50" /> <add name="ObjectId" elementType="int" length="10" /> <add name="ParentObjectId" elementType="int" length="10" /> <add name="SerialNumber" elementType="string" length="20" /> </fields> </table>
Создание обработчика собственного раздела конфигурации
Класс, который отвечает за обработку раздела конфигурации, должен быть унаследован от класса ConfigurationSection (нужно добавить ссылку на System.Configuration.dll). В нашем случае получаем следующее:
using System.Configuration; namespace UserSettings { ////// Обработчик секции, которая хранит описание файл с данными /// public class TableSection : ConfigurationSection { ////// Путь к файлу с данными /// [ConfigurationProperty("fileName", DefaultValue = "import.txt")] public string FileName { get { return (string)this["fileName"]; } set { this["fileName"] = value; } } ////// Кодировка файла /// [ConfigurationProperty("codePage", DefaultValue = "utf8")] public string CodePage { get { return (string)this["codePage"]; } set { this["codePage"] = value; } } ////// Коллекция столбцов таблицы /// [ConfigurationProperty("fields")] public FieldsCollection Fields { get { return (FieldsCollection)this["fields"]; } } } }
Так как у нас есть вложенная коллекция настроек, необходимо описать класс коллекции FieldsCollection, унаследованный от ConfigurationElementCollection, и элемент коллекции FieldElement, унаследованный от ConfigurationElement:
namespace UserSettings { ////// Обработчик коллекции данных (в нашем случае, коллекции полей) /// public class FieldsCollection : ConfigurationElementCollection { protected override ConfigurationElement CreateNewElement() { return new FieldElement(); } protected override object GetElementKey(ConfigurationElement element) { return ((FieldElement)element).Name; } } public class FieldElement : ConfigurationElement { ////// Имя столбца файла данных /// [ConfigurationProperty("name", IsRequired = true)] public string Name { get { return (string)this["name"]; } set { this["name"] = value; } } ////// Тип данных столбца /// [ConfigurationProperty("elementType", DefaultValue = "string")] public string ElementType { get { return (string)this["elementType"]; } set { this["elementType"] = value; } } ////// Длина поля /// [ConfigurationProperty("length", IsRequired = true)] public int Length { get { return (int)this["length"]; } set { this["length"] = value; } } } }
С помощью атрибута ConfigurationProperty декларативно описываются свойства конфигурации. Суть применения, я думаю, понятна из примера:
- Name - указывает название атрибута свойства (обязательный параметр, регистрозависимо)
- IsRequired - требуется ли обязательное указание описываемого свойства
- DefaultValue - значение свойства по умолчанию
Остальную информацию можно найти на msdn.
Кстати, вместо "самописных" коллекций можно использовать также стандартные коллекции, например, ConnectionStringSettingsCollection или NameValueConfigurationCollection. Причем использовать их можно как в самом классе-обработчике секции, так и в классе-элементе конфигурации.
Стоит также заметить, что класс ConfigurationSection является наследником от ConfigurationElement. Это значит, что, теоретически, обработчики разделов сами могут являтся и элементами коллекции.
Объявление обработчика раздела в файле конфигурации
Для связывания обработчика пользовательского раздела с именем раздела, в файле кофигурации необходимо добавить элемент configSections. Это своеобразные метаданные для менеджера конфигурации. В атрибуте type указывается тип и сборка, в которой лежит обработчик. Соответственно, в name указывается название созданной нами секции.
<configuration> <configSections> <section name="table" type="UserSettings.TableSection, UserSettings"/> </configSections> <!-- ... --> <configuration>
Кстати, хочу сделать здесь небольшое отступление. Обычно, когда файл конфигурации используется для хранения строки соединения с базой, содержимое файла выглядит примерно так:
<configuration> <connectionStrings> <add name="MyConnectionString" connectionString="Data Source=sqlserver;Initial Catalog=DevBase;User ID=sa;Password=123" providerName="System.Data.SqlClient" /> </connectionStrings> <configuration>
Может возникнуть резонный вопрос (по крайней мере, у меня возник): а где же здесь указан обработчик для секции connectionStrings? Ответ кроется в механизме иерархии и наследование файла конфигурации (подробнее). Если вкратце, то существуют различные области параметров конфигурации: некоторые действуют в глобальной области, другие — только на уровне приложения, корневого файла Web.config или Machine.config. Все приложения .NET Framework наследуют базовые параметры конфигурации и параметры по умолчанию от файла systemroot\Microsoft .NET\Framework\номер_версии\CONFIG\Machine.config. Открываем указанный файл и видим связывание обработчика с интересующей нас секцией:
<configuration> <configSections> <!-- ... --> <section name="connectionStrings" type="System.Configuration.ConnectionStringsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" requirePermission="false" /> </configSections> </configuration>
К слову, там же указан обработчик для секции appSettings. Но теперь вернемся к сути.
Добавление пользовательских элементов настройки в область параметров раздела конфигурации
Теперь соединим все вместе:
<configuration> <configSections> <section name="table" type="UserSettings.TableSection, UserSettings"/> </configSections> <connectionStrings> <add name="MyConnectionString" connectionString="Data Source=sqlexpress;Initial Catalog=DevBase;User ID=sa;Password=123" providerName="System.Data.SqlClient" /> </connectionStrings> <table fileName="files\import.txt" codePage="utf8"> <fields> <add name="Id" elementType="int" length="10" /> <add name="Name" elementType="string" length="100" /> <add name="UserName" elementType="string" length="50" /> <add name="ObjectId" elementType="int" length="10" /> <add name="ParentObjectId" elementType="int" length="10" /> <add name="SerialNumber" elementType="string" length="20" /> </fields> </table> </configuration>
Мы видим, как лаконично уживаются все настройки приложения в едином файле конфигурации. Это же так удобно: все настройки в одном месте. Не нужно тащить за приложением еще какие-либо настроечные файлы. А самое приятное, мы воспользовались встроенной возможностью .NET Framework. Но это еще не все. Теперь посмотрим как пользоваться этим в приложении.
Обращение к конфигурации из кода
Всю самую сложную работу мы выполнили. Теперь можно пользоваться результатами. Получить настройки из пользовательского раздела можно так:
class Program { static void Main(string[] args) { TableSection tableConfiguration = (TableSection)ConfigurationManager.GetSection("table"); Console.WriteLine(tableConfiguration.FileName); Console.WriteLine(tableConfiguration.CodePage); foreach (FieldElement field in tableConfiguration.Fields) { Console.WriteLine(string.Format("Имя поля: {0}, тип поля: {1}, длина поля: {2}", field.Name, field.ElementType, field.Length)); } Console.ReadLine(); } }
Единственное, что меня омрачает как разработчика на .NET Compact Framework, так это полное отсутствие данного механизма в указанной версии фрейморка. Там уж без написания своих собственных обработчиков не обойтись.
В следующий раз я покажу, как программно можно редактировать, создавать, удалять и сохранять файлы конфигурации. Также немного модифицируем класс TableSection для унификации вида настроек.
Ссылки:
Комментариев нет:
Отправить комментарий