В .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 для унификации вида настроек.
Ссылки:
Комментариев нет:
Отправить комментарий