65.9K
CodeProject 正在变化。 阅读更多。
Home

从零开始的自定义配置

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2012年7月2日

CPOL

6分钟阅读

viewsIcon

15859

downloadIcon

157

从自定义配置的概念到 VS 2010 中的智能感知。

Sample Image

引言

本文提供了创建自定义配置组所需的一切。我将详细介绍您必须执行的操作以及许多文章中经常忽略的要点。最终,您应该能够轻松上手并创建自己的配置。

背景

我发现有关创建自定义配置文件零散的信息已经很长时间了。本文旨在全面介绍您想了解的有关创建自己的自定义配置节的所有内容。

配置节

所有配置设置都从这里开始。您必须定义一个包含所有配置设置的节。在开始创建之前,我建议您粗略规划一下您的设置将是什么样的。这样做的原因是,您需要确定一组好的名称,需要知道您的节是包含属性还是仅包含集合,最后,它将成为您代码的模板。因此,这是我们在本文中将要做的示例

<applicationSection name= bork  active= true >
    <parameters>
        <add trigger= eventSent  type= myeventType, global.solutions.events  action= post  />
        <add trigger= eventComplete  type= myeventType, global.solutions.events  action= notify  />
        <add trigger= eventNotify  type= myeventType, global.solutions.events  action= send  />
    </parameters>
    <notifyLists>
        <add list= error  group= admins  server= mymail.global.solutions.com />
        <add list= warn  group= appGroup  server= mymail.globabl.solutions.com />
        <add list= friendly  group= customers  server= external.global.solutions.com />
  </notifyLists>
</applicationSection>

这些参数如何在程序中使用并不重要。重要的是类的命名以及结构的命名。我们拥有作为所有内容顶层的 ConfigurationSection。它是所有参数的主要容器。它将有两个属性和两个集合。在示例实现中,我们将类命名为与示例中的内容匹配。

public class applicationSection:ConfigurationSection
{
}

您可以立即看到您的配置与您的类之间的关系。在进一步定义此类之前,需要定义两个集合类。

[ConfigurationCollection( typeof( parameter ),
        CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap )]
public class parameters : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement ( )
    {
        return new parameter( );
    }

    protected override object GetElementKey ( ConfigurationElement element )
    {
        parameter internalElement = element as parameter;
        if ( internalElement != null )
            return internalElement.Trigger;
        else return null;
    }
}
[ConfigurationCollection( typeof( notification ),
    CollectionType = ConfigurationElementCollectionType.AddRemoveClearMap )]
public class notifyLists : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement ( )
    {
        return new notification( );
    }

    protected override object GetElementKey ( ConfigurationElement element )
    {
        notification internalElement = element as notification;
        if ( internalElement != null )
            return internalElement.Key;
        else return null;
    }
}

实现抽象类,您将获得两个方法。由您决定要将什么作为字典键。我的示例显示了提供该信息的两种方式。第一种方法 CreateNewElement() 返回一个包含类的集合的实例,第二种方法 GetElementKey() 定义您将用作键的内容。如果您的元素没有天然键的字段,您可以使用 GetHashCode 方法。 ConfigurationElementCollectionType 表明它将是一个集合,该集合会自动公开添加、替换和清除集合的功能。现在下一步是摆脱这些集合中的错误,因此让我们为每个元素定义类。元素是配置文件中的原子级别,仅包含属性。每个属性都由其上的装饰器唯一定义。

public class parameter : ConfigurationElement
{
    [ConfigurationProperty( "trigger", IsKey = true, IsRequired = true )]
    public string Trigger
    {
        get
        {
            return (string)this[ "trigger" ];
        }
        set
        {
            this[ "trigger" ] = value;
        }
    }
    [ConfigurationProperty( "type", IsKey = false, IsRequired = true )]
    public string TriggerType
    {
        get
        {
            return (string)this[ "type" ];
        }
        set
        {
            this[ "type" ] = value;
        }
    }
    [ConfigurationProperty( "action", IsKey = false, IsRequired = true )]
    public string TriggerAction
    {
        get
        {
            return (string)this[ "action" ];
        }
        set
        {
            this[ "action" ] = value;
        }
    }

}
public class notification : ConfigurationElement
{
    [ConfigurationProperty( "list", IsKey = false, IsRequired = true )]
    public string MailList
    {
        get
        {
            return (string)this[ " list " ];
        }
        set
        {
            this[ " list " ] = value;
        }
    }
    [ConfigurationProperty( "group", IsKey = false, IsRequired = false )]
    public string MailGroup
    {
        get
        {
            return (string)this[ "group" ];
        }
        set
        {
            this[ "group" ] = value;
        }
    }
    [ConfigurationProperty( "server", IsKey = false, IsRequired = true )]
    public string MailServer
    {
        get
        {
            return (string)this[ "server" ];
        }
        set
        {
            this[ "server" ] = value;
        }
    }
}

配置系统将使用反射来处理属性的填充,因此您无需担心任何事情,只需确保您的属性已正确装饰。既然一切都已定义,那么我到底该如何访问这些内容?在您的程序中,仍然使用 System.Configuration 命名空间,您将编写两行代码

Configuration config = ConfigurationManager.OpenExeConfiguration( ConfigurationUserLevel.None );
applicationSection section = config.Sections[ applicationSection ] as applicationSection;

第一行使您可以访问框架内置的 ConfigurationHandler。这将自动填充它在配置文件中看到的所有元素。第二行显示了预定义节的重要性。配置管理器可以填充每个节,并且已准备好将其交给任何数据使用者。如果您在定义中遇到问题,这很可能是您遇到异常的地方。如果您未正确定义您的节,第二行将返回一个 null 对象或缺少键的违规。如果您定义正确但对象定义有误,那么第一行将引发异常。

在示例应用程序中,您将看到我创建了一个名为 rawSectionvar,以便我可以检查返回的对象并发现我对象中的任何问题来源。我还要注意,如果您未能提供标记为 Required 的字段,第一行代码也将引发异常。在正常应用程序中,您将捕获该异常,进行检查,并向开发人员/用户返回关于缺少哪个配置部分的 P0 消息。还应注意,如果您为配置添加了数据验证,您也将在此处收到通知,形式为异常。

现在您已经有了配置节,您可以对其进行处理并进入应用程序执行阶段。

架构

由于我们从配置设置的表示开始,因此很容易从中生成模式。对于模式生成,我喜欢使用 http://www.xmlforasp.net/codebank/system_xml_schema/buildschema/buildxmlschema.aspx。它有一些局限性,例如没有可选字段,但它是免费的,而且做得相当不错!所以第一步是获取一个通用模式

< xml version="1.0" encoding="utf-8" >
<xsd:schema attributeFormDefault="unqualified" 
   elementFormDefault="qualified" version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="applicationSection">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="parameters">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element maxOccurs="unbounded" name="add">
                <xsd:complexType>
                  <xsd:attribute name="trigger" type="xsd:string" />
                  <xsd:attribute name="type" type="xsd:string" />
                  <xsd:attribute name="action" type="xsd:string" />
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
        <xsd:element name="notifyLists">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element maxOccurs="unbounded" name="add">
                <xsd:complexType>
                  <xsd:attribute name="list" type="xsd:string" />
                  <xsd:attribute name="group" type="xsd:string" />
                  <xsd:attribute name="server" type="xsd:string" />
                </xsd:complexType>
              </xsd:element>
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
      <xsd:attribute name="name" type="xsd:string" />
      <xsd:attribute name="active" type="xsd:boolean" />
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

下一步是将其导入 Visual Studio 并添加任何有效的数值、编辑和限制。我的最终模式看起来几乎相同,但 group 是可选的,list 具有枚举限制。

< xml version="1.0" encoding="utf-8" >
<xs:schema id="applicationSchema"
    elementFormDefault="qualified"
    xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="applicationSection">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="parameters">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="add">
                <xs:complexType>
                  <xs:attribute name="trigger" type="xs:string" />
                  <xs:attribute name="type" type="xs:string" />
                  <xs:attribute name="action" type="xs:string" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="notifyLists">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="add">
                <xs:complexType>
                  <xs:attribute name="list" >
                    <xs:simpleType >
                      <xs:restriction base="xs:string">
                        <xs:enumeration value="error"/>
                        <xs:enumeration value="warn" />
                        <xs:enumeration value="failure"/>
                        <xs:enumeration value="friendly" />
                      </xs:restriction>
                    </xs:simpleType>
                  </xs:attribute>
                  <xs:attribute name="group" type="xs:string" use="optional"/>
                  <xs:attribute name="server" type="xs:string" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
      <xs:attribute name="name" type="xs:string" />
      <xs:attribute name="active" type="xs:boolean" />
    </xs:complexType>
  </xs:element>
</xs:schema>

尝试让智能感知正常工作,时好时坏。一旦您创建了对定义您的模式和类库的库的引用,您就可以选择 app.config 上的空白区域,打开 Properties,然后在 schema 下拉列表中,您应该看到您的 http://tempuri.org 链接指向您的模式。虽然将此添加到属性页应该可以提供智能感知,但我未能体验到这一点。另一个选项是将您的模式放在与其他 Visual Studio 模式相同的文件夹中(VisualStudio10/xml/schemas),然后向 catalog.xml 文件添加新行

<Association extension= config  schema= %InstallRoot%/xml/schemas/applicationSchema.xsd  
   condition="starts-with($TargetFrameworkMoniker, '.NETFramework,Version=v4.') or $TargetFrameworkMoniker = '' />

我最终发现对我有效的方法是复制模式到模式文件夹**并**将其添加到 app.config 属性页的列表中,然后从列表顶部的未限定列表中选择它。这种方法的缺点是您必须编辑项目中的模式,保存它,然后将其复制并粘贴到 Visual Studio 文件夹中。但我得到了智能感知!我建议您在解决方案中添加注释,以便其他开发人员知道该怎么做。要测试这一点,请下载示例应用程序,将 XSD 文件复制到您的模式文件夹,然后将其添加到 app.config 的模式属性中。(如果尚未在项目中)当您添加一个新的 notifyList 元素时,list= 将提供一个有效值的列表供您选择。:)

关注点

所以本文从对我们的配置设置的粗略构想开始,然后我们创建了一个匹配配置设置的类结构,然后我们在第二个项目中实现了它,最后我们为配置文件的管理添加了智能感知。希望您觉得这很有价值。

© . All rights reserved.