http://unvkontakte.ath.cx
Нах удалил этот вконтакт!
вторник, 30 марта 2010 г.
четверг, 25 марта 2010 г.
Хачи
Вот ведь как странно, я случайно узнал:
хач = Хачик (армянское имя, уменьшительное) = Хачатур (оно же) = Хачатурян (фамилия распространённая)
хач = Хачик (армянское имя, уменьшительное) = Хачатур (оно же) = Хачатурян (фамилия распространённая)
вторник, 23 марта 2010 г.
Создание собственных секций (ConfigurationSection) в web.config/app.config
Конфигурационные файлы приложений (как веб, так и системных) предназначены для хранения настроек, причём любых. Но почему-то большинство программистов либо игнорирует эти возможности, либо пользуются только возможностью хранения строк соединения с базами данных (connection string) и пар «ключ-значение». Конфигурационный файл позволяет хранить данные любой вложенности и сложности, вопрос только в проектировании их структуры, а фактически – написание классов, которые будут сериализироваться.
Структура конфигурационных файлов условно делится на два раздела:
- Метаописание конфигурационного файла – раздел configSections .
- Непосредственно хранимые настройки.
Секция configSections – самая главная, так как она содержит в себе описание всех остальных секций, фактически это метаинформация о файле конфигурации. Именно этот раздел первым читает менеджер конфигураций, который считывает все описания секций и предназначенные для них структуры данных.
Если условно, то файл конфига – это база данных, а структуры данных – это объекты, поля которых содержат значения из базы данных. Роль менеджера конфигураций – своевременно заносить данные из БД в объект и обратно. С точки зрения программиста мы должны сделать две вещи:
- Описать структуру данных (класс и атрибуты привязки полей класса к параметрам xml файла);
- В конфигурационном файле сопоставить какое-либо название секции с нашей структурой.
Есть ещё и третий шаг, но он не обязательный – создать секцию и наполнить её данными. Необязательный этот шаг потому, что при описании структуры данных мы можем указать значения по умолчанию, что нам дасть возможность всегда иметь все настройки в структуре данных, независимо от того, есть ли они в конфиге.
К преимуществам конфигурационных файлов следует отнести то, что большую чатсь работы мы занимаемся декларационным программированием – фактически мы просто проектируем структуру, указываем, какие поля класса хранить в файле, совершенно не задумываясь о механизме сохранения и чтения, шифрования и т.п. Наличие в системе иерархии файлов конфигурации от самого низкого уровня машины(Machine.config) до конфигурационного файла приложения, и даже директории в веб-приложении, позволяет сделать систему наследования свойств и централизованного управления их значениями.
Лучше всего всё сказанное будет описано на примере простой секции, которая будет хранить некие данные, а также содержать в себе подсекции.
Начать лучше всего с создания главного класса секции, который должен быть унаследован от System.Configuration.ConfigurationSection (не забываем добавить в References System.Configuration.dll).
using System.Configuration;
<…>
///
/// Настройки SORM
///
public class SORMSettings: ConfigurationSection
{
///
/// Маска поиска
///
[ConfigurationProperty("SearchMask ", DefaultValue = "*")]
public String SearchMask
{
get { return (String)this["SearchMask"]; }
set { this["SearchMask"] = value; }
}
///
/// Стандартный адаптер для работы
///
[ConfigurationProperty("defaultAdapter", IsRequired = false)]
public String DefaultAdapterName
{
get { return (String)this["defaultAdapter"]; }
set { this["defaultAdapter"] = value; }
}
///
/// Список адаптеров
///
[ConfigurationProperty("adapters")]
public AdaptersCollection Adapters
{
get { return (AdaptersCollection) this["adapters"]; }
}
}
У нас есть собственный класс SORMSettings и три его свойства. Унаследовав класс от ConfigurationSection мы дали понять, что будем читать данные из конфигурационного файла, а атрибутами пометили свойства, которые и будут читаться.
Атрибут ConfigurationProperty указывает менеджеру конфигурации, что в данное свойства класса необходимо «синхронизировать» с конкретным атрибутом из файла конфигурации. У ConfigurationProperty имеется одно обязательное поле – name, которое указывает название атрибута секции из файла конфигурации в котором будет храниться наше значение.
public class SORMSettings: ConfigurationSection { [ConfigurationProperty("SearchMask")] public String SearchMask { get { return (String)this["SearchMask"]; } set { this["SearchMask"] = value; } } } | <configuration> <…> <SORM SearchMask ="123_*.dat"> <…> configuration> |
Помимо свойства name у ConfigurationProperty есть парочка других полезных необязательных свойств:
- DefaultValue – значение по умолчанию, если значение атрибута в файле конфигурации не определено.
- IsRequired – указываем, что значение должно быть задано в файле конфигурации обязательно.
- IsKey – позволяет отметить поле как ключевое, в случае, когда мы храним набор однотипных элементов.
Для хранения сложных вложенных структур данных в секции нужно создать класс-наследник уже от ConfigurationElement. В нашем примере это класс AdaptersCollection унаследованный от ConfigurationElementCollection (ConfigurationElementCollection – это наследник ConfigurationElement).
///
/// Коллекция адаптеров
///
public class AdaptersCollection: ConfigurationElementCollection
{
///
/// Создание нового элемента коллекции нужно типа
///
///
protected override ConfigurationElement CreateNewElement()
{
return new AdapterElement();
}
///
/// Получение ключа элемента для его идентификации
///
///
///
protected override object GetElementKey(ConfigurationElement element)
{
return ((AdapterElement) element).Name;
}
///
/// Получение элемента по его индексу
///
///
///
public AdapterElement this[Int32 index]
{
get { return (AdapterElement) BaseGet(index); }
set
{
if (BaseGet(index)!= null)
BaseRemoveAt(index);
BaseAdd(index,value);
}
}
///
/// Получение элемента по его имени
///
///
///
public new AdapterElement this[String name]
{
get { return (AdapterElement) BaseGet(name); }
}
}
Класс AdaptersCollection – описывает список адаптеров которые мы будет хранить в настройках. Минимально, что необходимо сделать унаследовавшись от ConfigurationElementCollection это переопределить метод CreateNewElement() которая вызывается при наполнении коллекции элементами. Мы должны внутри этой функции инициализировать новый элемент коллекции и вернуть его. Помимо этого мы можем переопределить множество функций отвечающих за управление коллекцией (добавление, удаление, очистка, поиск). В данном случае ограничимся введением двух индексаторов для доступа к элементам коллекции по имени и по индексу. Наша коллекция работает с элементами AdapterElement, вот код класса:
/// <summary>
/// Элемент адаптера
///
public class AdapterElement: ConfigurationElement
{
///
/// Название адаптера
///
[ConfigurationProperty("name")]
public String Name
{
get { return (String) this["name"]; }
set { this["name"] = value;}
}
///
/// Строка с описанием типа адаптера
///
[ConfigurationProperty("type")]
public String Type
{
get { return (String)this["type"]; }
set { this["type"] = value; }
}
///
/// Параметры для адаптера
///
[ConfigurationProperty("parameters", IsDefaultCollection = true)]
public NameValueConfigurationCollection Parameters
{
get { return (NameValueConfigurationCollection) this["parameters"]; }
}
}
Элемент содержит два свойства – Name и Type, а также ещё одну вложенную коллекцию типа NameValueConfigurationCollection. NameValueConfigurationCollection – это коллекция элементов NameValueConfigurationElement, которые описываются двумя свойствами – Name (ключевое поле) и Value (значение). Есть аналогичная коллекция KeyValueConfigurationCollection – там поля Key и Value, видно из названия.
Таким образом, мы имеем адаптер, который хранит в себе набор (словарь) каких-то параметров (Parameters), имя (Name) и тип (Type). Адаптеры хранятся в коллекции (Adapters) нашей секции, где также указывается имя стандартного адаптера (DefaultAdapterName) и маска поиска (SearchMask). Вот так выглядит код секции:
<SORM defaultAdapter=" My Simple XML Adapter">
<adapters>
<add name="My Simple XML Adapter" type="SimpleXMLAdapter">
<parameters>
<add name="DataBasePath" value="DataBase" />
<add name="LogLevel" value="3" />
parameters>
add>
adapters>
SORM>
Как видите, структура хранения данных идентична структуре классов, что позволяет довольно быстро делать удобные конфигурационные классы секций.
Осталось лишь объяснить менеджеру конфигураций, что раздел SORM нужно сопоставить с нашим классом SORMSettings. Для этого в разделе configSections конфигурационного файла опишем нашу секцию:
<section
name="SORM"
type="Polyakov.SORM.Settings.SORMSettings, Polyakov.SORM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
/>
Параметры section:
- Name – это то, как будет называться наше секция.
- Type – тип объекта, который будет предоставлять доступ к данным конфигурации – это наш класс секции.
Это всё! Для красоты и удобства мы можем группировать секции, для этого необходимо в configSections создать элемент sectionGroup и уже в него помещать секции.
Обратиться к данным конфигурации можно из кода так:
var cfg = (Settings.SORMSettings)ConfigurationManager.GetSection("SORMSettings/SORM");
if (cfg == null)
return;
Console.WriteLine(cfg.DefaultAdapterName);
А файл конфигурации будет выглядеть так:
<configuration>
<configSections>
<sectionGroup name="SORMSettings">
<section
name="SORM"
type="Polyakov.SORM.Settings.SORMSettings, Polyakov.SORM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
allowLocation="true"
allowDefinition="Everywhere"
/>
sectionGroup>
configSections>
<SORMSettings>
<SORM defaultAdapter="SimpleXMLAdapter">
<adapters>
<add name="SimpleXMLAdapter" type="Polyakov.SORM.Adapters.XML.SimpleXMLAdapter, Polyakov.SORM, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<parameters>
<add name="DataBasePath" value="DataBase" />
parameters>
add>
adapters>
SORM>
SORMSettings>
configuration>
Подписаться на:
Сообщения (Atom)