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

从 .NET 2.0 ConfigurationSection 子类中提取 XML Schema,用于 Visual Studio 中的配置验证和 IntelliSense

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (10投票s)

2006 年 8 月 1 日

LGPL3

12分钟阅读

viewsIcon

133263

downloadIcon

863

本文将介绍如何使用我编写的 XSDExtractor 应用程序,从 .NET 2.0 ConfigurationSection 子类中提取 XML Schema (XSD)。文章还将展示如何使用 XSD 文件为 Visual Studio 提供验证和 IntelliSense 支持。

Screenshot - screenshot.GIF

引言

.NET 2.0 为 System.Configuration 命名空间 带来了重大变化。有很多文章已经介绍了使用配置类的新方法。本文将不试图通过复制其内容来夺走它们的风头。相反,本文将介绍我编写的一个实用程序,它有助于弥补新配置模型中的一个差距:在实际编辑配置文件的同时,提供对其的 IntelliSense 和验证。

必备组件

假设您已经熟悉 .NET 2.0 的 System.Configuration 命名空间。如果您不熟悉,请参阅 Luis Angel R.C. 的这篇文章

目录

示例

在本文中,我将引用一个包含自定义配置节的示例项目。该项目是一个简单的 FileWatcher 应用程序,它根据配置设置监视多个文件/文件夹。这些设置由一个自定义配置节处理,该配置节已为其生成了 XSD。您可以从上面的链接之一下载该项目。

详细信息

在我看来,对开发者生产力最好的改进之一就是 IntelliSense。Microsoft 在 Visual Studio 2002 中为 XML 编辑器添加了 IntelliSense 功能。该编辑器会提示您自动完成元素和属性,只要 XML 具有 Visual Studio 已知的 XML Schema 支持。现在,随着 Visual Studio 2005 和 .NET 2.0 配置框架的推出,我们在框架中拥有了一个强大的配置命名空间。新配置命名空间的问题在于,配置基于 ConfigurationSection 类而不是 XML Schema。因此,XML 编辑器无法帮助我们完成配置元素。

这就是我编写 XSDExtractor 应用程序的直接原因。我的另一个个人项目大量使用了配置文件,我希望确保人们能够轻松配置应用程序。维护大量的 XML 配置文件并不有趣,而且您无法从 IDE 获得任何帮助。我还希望能够远程编辑配置文件,但仍然能够验证文件。这意味着要么将 ConfigurationSection 类的副本发送给远程客户端,要么发送 XML Schema。我想您可以猜到我选择了哪一个。

本文附带的 XSDExtractor 程序(源代码可从上方下载)通过反射,从已编译的 ConfigurationSection 类型创建 XML Schema 文件。然后,在编写配置文件时可以使用生成的 XSD 文件为编辑器提供 IntelliSense。

重要的架构代码

XSDExtractor 应用程序由解析器组成。在 XSDExtractor 的意义上,解析器能够读取已编译程序集中的元数据,然后适当地处理它。System.Configuration 命名空间包含编写自定义元素、集合和属性的支持。因此,我使用 TypeParser 继承模型复制了此模型。基类显示如下

TypeParser 抽象类充当应用程序中所有 TypeParser 的基类。此外,通过调用 TypeParserFactory 类上的一个 static 方法来实例化类型解析器。创建正确解析器的责任委托给工厂类。调用者所知道的只是来自工厂类的返回值继承自 TypeParser

应用程序中的其他重要代码位于 Validator 模型中。System.Configuration 命名空间包含可以限制存储在配置字段中的数据类型的属性。幸运的是,XML Schema 规范也足够强大,可以允许限制字段中的数据。因此,Validator 模型负责将验证器属性转换为 XML Schema 简单类型,并附带限制字段可用值的方面。下面的 SimpleType 是从 ConfigurationSection 类中的枚举创建的。

TypeParser 模型类似,Validator 模型也使用 Factory 类来实例化验证器。factory 对象返回 ValidatorAttributeParser 的实例。嗯,实际上是它的一个子类

这个类层次结构处理 ValidatorAttribute 系列属性。这些属性可以放在 ConfigurationSection 的任何属性上,并用于限制被视为可接受的配置数据。例如,您可以将整数的值限制在某个范围内。如果配置文件中输入的值超出范围,则会引发异常。ValidatorAttributeParser 类的每个子类都处理 System.Configuration 命名空间中的特定属性。有关这些类的更多详细信息,请参阅下面的局限性部分。

System.Xml.Schema 命名空间

当我开始编写 XSDExtractor 应用程序时,我认为我将不得不编写自己的类来模仿 XML Schema 语言的功能,例如 ComplexTypesSimpleTypesElements 等。事实证明,.NET 提供了 System.Xml.Schema 命名空间,其中包含了我需要的所有类。这使我能够使用丰富的对象集合来构建输出的 XML Schema。

我花了一些时间才理解如何表示一个元素属于特定复杂类型,或者一个属性属于简单类型。对于有兴趣的人来说,这就是我遵循的简单规则

要将对象的类型设置为现有全局类型(无论是在文档中已知还是通过导入的命名空间),我将 SchemaTypeName 设置为等于 XmlQualifiedName 对象,如下所示

<object>.SchemaTypeName = new XmlQualifiedName("xs:string") 

对于仅供该对象使用的对象,我会将 SchemaType 属性设置为等于 new XmlSchemaComplexTypeXmlSchemaSimpleType,如下所示

<object>.SchemaType = new XmlSchemaSimpleType(); 

一旦我克服了这些差异,Xml.Schema 命名空间就变得易于使用。

使用 XSDExtractor

首先,将实用程序解压到其自己的文件夹中。XSDExtractor 是一个命令行项目,因此必须在命令行上传递参数。如果您在没有参数的情况下运行 XSDExtractor,它会询问您是否可以转换当前目录及任何子目录中发现的所有程序集中的所有 ConfigurationSection 子类。如果您希望将许多 ConfigurationSection 类转换为 XSD,则推荐使用此方法。XSD 将保存在包含 ConfigurationSection 类的程序集的相同目录中。它将以与 ConfigutationSection 类名相同的名称命名,以防止文件名冲突。以下是该实用程序接受的命令行参数列表

Switch 详细信息 示例
/a 指定要检查的程序集 /a C:\myassembly.dll
/c 要检查的类名 /c MyNamespace.MyClass
/r 根命名空间元素。将是 Xsd 中的第一个元素的名称。如果省略,则使用 ConfigurationSection 类名。 /r MyRoot
/s 静默。不提示确认覆盖或任何决定。 /s true

如何使用 XSDExtractor 生成的 XSD

Visual Studio 2005 配备了一个强大的 XML 编辑器。要为您的配置节获得 IntelliSense,只需在根元素中添加 xmlns 属性即可。这将使编写配置节的工作变得更加容易。Visual Studio 2003 做了类似的事情,但它假设您正在从根处编写文档。不幸的是,当您编写配置节时,情况并非如此。如下图所示,在配置根元素中添加 xmlns 属性可使 Visual Studio 2005 提供 IntelliSense 支持。

添加 xmlns 属性确实会导致一个不幸的副作用:您的配置将无法加载!之所以无法加载,是因为配置加载程序会检测到该属性,然后告诉您它包含一个未知的属性“xmlns”。这实际上是一个非常有用的功能,因为如果您错拼了任何属性,您会立即知道。解决方案是在您的配置中添加一个可选的 string 属性,如下所示

[ConfigurationProperty("xmlns", IsRequired = false)]
public string Xmlns {
  get { return (string)base["xmlns"];}
}

我认为最好将此属性添加到您从所有 ConfigurationSection 子类继承的基类中,这样您就可以不加思索地获得该属性。

它是如何工作的?

使用 MultiFileWatcher 示例应用程序的源代码,您会发现 ConfigurationSection 类相对简单。

  public class MultiWatcherConfigurationSection  : ConfigurationSection {

    /* 
     Xml Namespace attribute. Added so that the parser doesnt complain
    */
    [ConfigurationProperty("xmlns", IsRequired = false)]
    public string Xmlns {
      get { return (string)base["xmlns"]; }
    }

    /* 
      Collection of files that will be monitored by the application
    */
    [ConfigurationProperty("files", 
        IsDefaultCollection=true, IsKey=false, IsRequired=true)]
    public FilesCollection Files {
      get { return (FilesCollection)base["files"]; }
    }
  } 

它包含两个属性,我在上一节中提到的 xmlns 属性,以及一个允许在配置中出现多个元素的集合。

XSDExtractor 使用程序集中的类型和属性来构建 XML Schema。在上面的示例中,XSDExtractor 首先会检测到 MultiWatcherConfigurationSection 类实际上是 ConfigurationSection 类的子类。然后,它会检查该类型的**所有** public 属性,并检查哪些属性带有 [ConfigurationProperty] 属性。这些属性是配置文件中预期的(尽管有时是可选的)元素。

属性和元素之间的关系很简单。如果属性返回内置类型(如 intstringbool 等),则将其视为其包含元素的属性。如果属性返回 ConfigurationElement 类型,则将其视为其包含元素的子元素。最后,如果属性返回 ConfigurationElementCollection 类型,则它也是其包含元素的子元素,但有特殊的规则应用于粒子元素。

因此,根据上述规则,预期配置将在根配置元素中包含一个 string 类型的属性,以及一个(可重复的)FilesCollection 的子元素。当然,FileCollection 还包含有关子元素的进一步信息。以下是 FileCollection 类的片段

 [ConfigurationCollection(typeof(FileElement), 
                          AddItemName="add", 
                          CollectionType 
                          = ConfigurationElementCollectionType.BasicMap)]
 public class FilesCollection : ConfigurationElementCollection {
    .
    .

我们感兴趣的部分是 [ConfigurationCollection] 属性。这告诉我们集合将由 FileElement 类型的 ConfigurationElement 组成,并且每个 FileElement 的元素名称实际上是“add”。CollectionType 属性告诉我们此集合没有 remove 或 clear 选项;您可以简单地添加,仅此而已。以下是集合可能外观的示例

<files> 元素由 MultiWatcherConfigurationSection 类中的以下代码片段确定

 [ConfigurationProperty("files", 
     IsDefaultCollection=true, IsKey=false, IsRequired=true)]

<add> 元素代表由 FilesCollection 类中的以下代码片段确定的 FileElement 类型。

 [ConfigurationCollection(typeof(FileElement), 
                          AddItemName="add",
                          CollectionType 
                          = ConfigurationElementCollectionType.BasicMap)]

XSDExtractor 使用此元数据和预期的 XML 结构来构建一个与 XML 结构匹配的 XML Schema。它还使用递归算法来构建匹配具有许多嵌套和子嵌套类型的 ConfigurationSection 类的 XML Schema。

限制

创建自定义 ConfigurationSection 类有两种方法。第一种是声明式模型,其中每个元素都用 ConfigurationPropertyConfigurationCollection 属性进行装饰。第二种是编程模型,其中各个元素在代码中手动添加。由于 XSDExtractor 的工作方式,它只会为使用第一种模型(声明式模型)的 ConfigurationSection 创建 XML Schema。如果您混合使用这两种模型,XSDExtractor 仍然可以工作,但生成的 XML Schema 可能不准确。

另一个限制是 ValidatorAttributeParser 类层次结构仅支持解析 .NET 2.0 随附的标准验证器。如果您的 ConfigurationSection 包含一个定制的验证属性,那么 XSDExtractor 将无法处理。在这种情况下,默认行为是返回一个不受限制的 XmlSchemaSimpleType

摘要

希望您觉得这个实用程序很有用,并且它能帮助您更快地编写配置文件。如果您想了解我在另一个应用程序中如何使用 XSDExtractor 应用程序的功能,请查看我在 Sourceforge 上的JFDI 项目(一个 .NET 2.0 作业框架)。任何反馈都非常欢迎。

历史

  • 2007 年 5 月 17 日
    • 文章已编辑并发布到 CodeProject.com 主文章库

  • 2006 年 9 月 24 日
    • 发布版本 1.1.1
      修复:增强功能
      • 一个类中多次使用的集合会被错误地添加到架构中。感谢 Idael Cardoso 纠正了这个问题并提供了一个演示该问题的类。(添加了单元测试来重现 bug / 证明修复有效)
      • 项目中添加了 Nant 兼容的生成脚本
  • 2006 年 8 月 10 日
    • 发布版本 1.1
      修复:增强功能
      • /R 开关现在可以正常工作
      • StringValidatorAttribute 具有无效字符列表为空的字符串不再导致生成空的 pattern 元素(这会导致 XSD 无效)
      • 现在会忽略 xmlns 属性
      • 文档现在已添加到 XSD。字段类型、默认值以及字段是否必需的标准信息现在显示在 VS2005 XML 编辑器的工具提示中。如果属性带有 System.ComponentModel.DescriptionAttribute,则描述也会附加到默认信息中。
      • 正确处理默认集合,例如 HttpModules 配置节
      • 有用的信息作为注释添加到 XSD 中,以便可以看到 XSD 是如何生成的,由谁生成的,以及何时生成的
  • 2006 年 8 月 1 日
    • 初始文章

许可

该实用程序和源代码可免费使用,并根据 GNU Lesser General Public License 发布。它们都附带了关于使用限制的 usual yada-yada。如果您发现该实用程序/源代码很有用,我只希望您在您的应用程序/博客/其他地方提及本文!

© . All rights reserved.