Создание собственного сложного обработчика раздела
Простой обработчик раздела бывает полезен, но иногда возникают ситуации, когда требуется сохранять не только простые значения. Используя собственный обработчик раздела, можно сохранять большое количество параметров в объектах коллекций или возвратить совершенно другой тип объекта. Ниже мы рассмотрим, как использовать один раздел для хранения нескольких групп параметров, а также как использовать эти параметры для предоставления объектов для других классов.
В следующих примерах мы создадим раздел настроек для вымышленного веб-приложения. Это приложение называется DNetReporting; оно создает отчеты на основе различных источников данных, а затем либо печатает эти отчеты, либо передает их по электронной почте. Для хранения нескольких настроечных параметров приложения задействуем разделы настроек, а также обработчики разделов и другие классы. Первый шаг состоит в создании раздела настроек, который включает несколько используемых параметров.
<DNetReporting> <License key="b12gfdt32d2" /> <DataSources> <DataSource name="Employee Database" connstring="" /> <DataSource name="Sales Database" connstring="" /> </DataSources> <Emails> <Email name="James Avery" address="javery@infozerk.com" /> <Email name="Accountants" address="accounts@infozerk.com" /> </Emails> <Printers> <Printer name="IT Printer" localname="HP Deskjet 1200" /> <Printer name="HR Printer" localname="HP Deskjet 4050" /> </Printers> </DNetReporting>
Данный раздел настроек хранит ключ лицензии приложения, все источники данных, адреса электронной почты и принтеры. Следующий шаг заключается в создании записи для этого раздела настроек в разделе <configSections>.
<configSections> <section name="DNetReporting" type="DNetReporting.ConfigHandler, DNetReporting"/> </configSections>
Теперь мы создадим обработчик раздела, считывающий эти параметры из настроечного файла. Ниже приведен код основной части обработчика раздела, сильно напоминающий обработчик раздела, созданный ранее.
C#
using System; using System.Data; using System.Data.SqlClient; using System.XML; using System.Configuration;
namespace DNetReporting { public class ConfigHandler : IConfigurationSectionHandler { public object Create(object parent, object configContext, XmlNode section) { string sHomePage; sHomePage = section.Attributes.Item[0].Value; return sHomePage; } } }
VB.NET
Imports System Imports System.Data Imports System.Data.SqlClient Imports System.XML Imports System.Configuration
Namespace DNetReporting Public Class ConfigHandler Implements IConfigurationSectionHandler Public Function Create(parent As Object, configContext As _ Object, section As XmlNode) As Object Implements _ IConfigurationSectionHandler.Create Dim sHomePage As String sHomePage = section.Attributes.Item(0).Value Return sHomePage End Function End Class End Namespace
Обработчик раздела настроек должен иметь возможность унаследовать все родительские настроечные параметры. Первый параметр типа object, который называется parent, содержит параметры, наследуемые этим обработчиком. Мы сохраняем параметры в объекте Hashtable, так что через объект parent будет передаваться именно этот объект. Ниже приведен код, который следует добавить в метод Create, чтобы реализовать наследование данных.
C#
Hashtable parentConfig = (Hashtable) parent; Hashtable config; if (parentConfig != null) { config = (Hashtable) parentConfig.Clone(); } else { config = new Hashtable(); }
VB.NET
Dim parentConfig As Hashtable = CType(parent, Hashtable) Dim config As Hashtable If Not (parentConfig Is Nothing) Then config = CType(parentConfig.Clone(), Hashtable) Else config = New Hashtable() End If
Первая строка кода преобразует тип объекта parent к типу Hashtable. Вторая строка создает Hashtable, в котором будут храниться параметры. Оператор If, с которого начинается третья строка кода, проверяет, равен ли parentConfig типа Hashtable значению null.
Если Hashtable существует, то имеются параметры, которые должны быть унаследованы новым набором параметров. Если родительский Hashtable существует, то новый Hashtable будет клоном родительского Hashtable; если родительский Hashtable не существует, то будет создан новый Hashtable.
Следующим шагом в создании собственного обработчика раздела является перебор всех элементов XML и определение того, что следует делать с различными подразделами и элементами. Ниже приведен код для перебора всех элементов XML этого раздела.
C#
foreach (XmlNode child in section.ChildNodes) { if(child.NodeType != XmlNodeType.Comment && child.NodeType != XmlNodeType.Whitespace) { switch (child.Name) { case "License": config["License"] = child.Attributes.Item(0).Value; break;
case "DataSources": config["DataSources"] = ReadSection( config["DataSources"], child, "connstring"); break;
case "Emails": config["Emails"] = ReadSection(config["Emails"], child, "address"); break;
case "Printers": config["Printers"] = ReadSection(config["Printers"], child, "localname"); break; } } }
VB.NET
Dim child As XmlNode For Each child In section.ChildNodes If child.NodeType <> XmlNodeType.Comment And child.NodeType <> _ XmlNodeType.Whitespace Then Select Case child.Name Case "License" config("License") = child.Attributes.Item(0).Value
Case "DataSources" config("DataSources") = ReadSection( _ config("DataSources"), child, "connstring")
Case "Emails" config("Emails") = ReadSection(config("Emails"), _ child, "address")
Case "Printers" config("Printers") = ReadSection(config("Printers "), _ child, "localname") End Select End If Next child
Теперь мы создадим обработчик раздела, считывающий эти параметры из настроечного файла. Ниже приведен код основной части обработчика раздела, сильно напоминающий обработчик раздела, созданный ранее.
C#
using System; using System.Data; using System.Data.SqlClient; using System.XML; using System.Configuration;
namespace DNetReporting { public class ConfigHandler : IConfigurationSectionHandler { public object Create(object parent, object configContext, XmlNode section) { string sHomePage; sHomePage = section.Attributes.Item[0].Value; return sHomePage; } } }
VB.NET
Imports System Imports System.Data Imports System.Data.SqlClient Imports System.XML Imports System.Configuration
Namespace DNetReporting Public Class ConfigHandler Implements IConfigurationSectionHandler Public Function Create(parent As Object, configContext As _ Object, section As XmlNode) As Object Implements _ IConfigurationSectionHandler.Create Dim sHomePage As String sHomePage = section.Attributes.Item(0).Value Return sHomePage End Function End Class End Namespace
Обработчик раздела настроек должен иметь возможность унаследовать все родительские настроечные параметры. Первый параметр типа object, который называется parent, содержит параметры, наследуемые этим обработчиком. Мы сохраняем параметры в объекте Hashtable, так что через объект parent будет передаваться именно этот объект. Ниже приведен код, который следует добавить в метод Create, чтобы реализовать наследование данных.
C#
Hashtable parentConfig = (Hashtable) parent; Hashtable config; if (parentConfig != null) { config = (Hashtable) parentConfig.Clone(); } else { config = new Hashtable(); }
VB.NET
Dim parentConfig As Hashtable = CType(parent, Hashtable) Dim config As Hashtable If Not (parentConfig Is Nothing) Then config = CType(parentConfig.Clone(), Hashtable) Else config = New Hashtable() End If
Первая строка кода преобразует тип объекта parent к типу Hashtable. Вторая строка создает Hashtable, в котором будут храниться параметры. Оператор If, с которого начинается третья строка кода, проверяет, равен ли parentConfig типа Hashtable значению null.
Если Hashtable существует, то имеются параметры, которые должны быть унаследованы новым набором параметров. Если родительский Hashtable существует, то новый Hashtable будет клоном родительского Hashtable; если родительский Hashtable не существует, то будет создан новый Hashtable.
Следующим шагом в создании собственного обработчика раздела является перебор всех элементов XML и определение того, что следует делать с различными подразделами и элементами. Ниже приведен код для перебора всех элементов XML этого раздела.
C#
foreach (XmlNode child in section.ChildNodes) { if(child.NodeType != XmlNodeType.Comment && child.NodeType != XmlNodeType.Whitespace) { switch (child.Name) { case "License": config["License"] = child.Attributes.Item(0).Value; break;
case "DataSources": config["DataSources"] = ReadSection( config["DataSources"], child, "connstring"); break;
case "Emails": config["Emails"] = ReadSection(config["Emails"], child, "address"); break;
case "Printers": config["Printers"] = ReadSection(config["Printers"], child, "localname"); break; } } }
VB.NET
Dim child As XmlNode For Each child In section.ChildNodes If child.NodeType <> XmlNodeType.Comment And child.NodeType <> _ XmlNodeType.Whitespace Then Select Case child.Name Case "License" config("License") = child.Attributes.Item(0).Value
Case "DataSources" config("DataSources") = ReadSection( _ config("DataSources"), child, "connstring")
Case "Emails" config("Emails") = ReadSection(config("Emails"), _ child, "address")
Case "Printers" config("Printers") = ReadSection(config("Printers "), _ child, "localname") End Select End If Next child
Данный код для перебора всех узлов XML, найденных в разделе настроек, использует оператор For.
Для проверки того, что каждый узел XML в разделе не является комментарием или пробелом, используется следующий код.
If child.NodeType <> XmlNodeType.Comment And child.NodeType <> _ XmlNodeType.Whitespace Then
Если узел не является комментарием или пробелом, оператор case проверяет имя узла и в зависимости от его значения выполняет определенное действие с узлом. Например, если узел называется "License", значение узла добавляется в Hashtable в ключ "License".
Для трех других типов узлов (принтеры, электронная почта и источники данных) оператор case вызывает внешний метод, так как для каждого узла возможно наличие нескольких значений. Для их обработки внешний метод создает для каждого из этих параметров Hashtable, который добавляется в Hashtable настроек. Ниже приведен метод ReadSection, который создает Hashtable, содержащий все значения для каждого раздела. Чтобы разместить все имена значений из каждого раздела, третий параметр определяет строку атрибутов значения, которые должны использоваться для каждого раздела.
C#
private Hashtable ReadSection(object parent, XmlNode section, string sValueAttribute) { Hashtable parentConfig = (Hashtable) parent; Hashtable config; if (parentConfig != null) { config = (Hashtable) parentConfig.Clone(); } else { config = new Hashtable(); } foreach (XmlNode child in section.ChildNodes) { if(child.NodeType != XmlNodeType.Comment && child.NodeType != XmlNodeType.Whitespace) { config[child.Attributes.GetNamedItem("name").Value] = child.Attributes.GetNamedItem(sValueAttribute).Va lue; } } return config; }
VB.NET
Private Function ReadSection(parent As Object, section As XmlNode, _ sValueAttribute as string) As Hashtable Dim parentConfig As Hashtable = CType(parent, Hashtable) Dim config As Hashtable If Not (parentConfig Is Nothing) Then config = CType(parentConfig.Clone(), Hashtable) Else config = New Hashtable() End If Dim child As XmlNode For Each child In section.ChildNodes If child.NodeType <> XmlNodeType.Comment And _ child.NodeType <>XmlNodeType.Whitespace Then config(child.Attributes.GetNamedItem("name").Value) = _ child.Attributes.GetNamedItem(sValueAttribute).Value End If Next child Return config End Function
Первая часть этого метода в точности совпадает с кодом метода Create. Она используется для наследования всех параметров, установленных в предыдущих разделах настроек. Оператор For перебирает все дочерние узлы раздела и добавляет значения в Hashtable. Затем этот Hashtable возвращается и добавляется в Hashtable, созданный в методе Create.
На этом обработчик раздела заканчивается, но было бы здорово предоставить класс, который осуществляет доступ к этим коллекциям. Ниже приведен код примера класса для доступа к данным коллекциям без вызова метода GetConfig.
C#
using System; using System.Collections; using System.Configuration;
namespace DNetReporting { public class ConfigSettings { public static Hashtable Printers() { Hashtable config = GetConfig(); return (Hashtable) config["Printers"]; }
public static Hashtable Emails() { Hashtable config = GetConfig(); return (Hashtable) config["Emails"]; }
public static Hashtable DataSources() { Hashtable config = GetConfig(); return (Hashtable) config["DataSources"]; }
public static string License() { Hashtable config = GetConfig(); string sLicense = config["License"].ToString(); return sLicense; }
private static Hashtable GetConfig() { Hashtable configTable = (Hashtable) ConfigurationSettings.GetConfig("DNetReporting"); return configTable; } } }
VB.NET
Imports System Imports System.Collections Imports System.Configuration
Namespace DNetReporting
Public Class ConfigSettings
Public Shared Function Printers() As Hashtable Dim config As Hashtable = GetConfig() Return CType(config("Printers"), Hashtable) End Function
Public Shared Function Emails() As Hashtable Dim config As Hashtable = GetConfig() Return CType(config("Emails"), Hashtable) End Function
Public Shared Function DataSources() As Hashtable Dim config As Hashtable = GetConfig() Return CType(config("DataSources"), Hashtable) End Function
Public Shared Function License() As String Dim config As Hashtable = GetConfig() Dim sLicense As String = config("License").ToString( ) Return sLicense End Function
Private Shared Function GetConfig() As Hashtable Dim configTable As Hashtable = _ CType(ConfigurationSettings.GetConfig("DNetReport ing"), _ Hashtable) Return configTable End Function End Class End Namespace
Данный класс предоставляет несколько методов для доступа к объектам Hashtable, избавляя от необходимости работать напрямую с обработчиком раздела настроек. Например, если нужно заполнить значениями из Hashtable адресов электронной почты раскрывающийся список, то можно воспользоваться следующим кодом.
C#
Hashtable htEmail = ConfigSettings.Emails(); DropDownList2.DataTextField = "Key"; DropDownList2.DataValueField = "Value"; DropDownList2.DataSource = htEmail; DropDownList2.DataBind();
VB.NET
Dim htEmail As Hashtable = ConfigSettings.Emails() DropDownList2.DataTextField = "Key" DropDownList2.DataValueField = "Value" DropDownList2.DataSource = htEmail DropDownList2.DataBind()
Данный код создает экземпляр Hashtable с адресами почты, а затем связывает его с раскрывающимся списком.
Этот усложненный обработчик раздела показывает, как хранить в настроечном файле несколько разделов и параметров, а затем получать к ним доступ из приложения. Это позволяет избежать использования для хранения всего лишь пары значений базы данных и вносить изменения в эти параметры без перекомпиляции приложения.