企业配置管理器






4.58/5 (25投票s)
SysConfiguration 是一个组件,允许存储企业应用程序的配置数据。它旨在成为 System.Configuration 类或企业库(Microsoft Patterns and Practices)的改进版本。
引言
SysConfiguration 是一个组件,允许存储企业应用程序的配置数据。它旨在成为 System.Configuration
类或企业库(Microsoft Patterns and Practices)的改进版本。
企业配置
我在 T4G Limited 的一个团队工作,该团队为大型企业创建复杂的定制应用程序。单个应用程序拥有多个用户界面(消费者、呼叫中心、B2B 等)并不少见。因此,我们经常有多个架构(Web、Web Services、WinForms、COM+ 等)并行运行。作为一家技术服务公司,需求波动性很高,这意味着我们经常需要部署到多个环境(开发、测试、预发布、用户验收测试、生产等)。根据我们所做的项目类型,此组件的目标是
- 自动检测配置设置是否缺失
- 问题:App.Config 的一个大问题是它是松散类型的。这意味着,如果某个组件使用了某个设置,您必须实际测试该组件才能确定它是否存在。
- 解决方案:SysConfiguration 使用强类型类。首次加载时,它将根据架构验证配置数据,以确保所有强制设置都存在,同时仍允许可选设置。这在代码在不同环境(例如,从开发到测试再到生产)之间移动时是一个巨大的优势。
- 适用于多种架构
- 问题:如果您的应用程序有多种架构,每种架构都将配置存储在不同的位置:Web 应用程序和 Web 服务使用 Web.config,WinForms 和控制台应用程序使用 App.config,COM+ 使用注册表等。企业库确实有
FileConfigurationSource
类,它允许您调用一个集中的配置文件,这是对默认 .NET Framework 的改进。 - 解决方案:SysConfiguration 可以为所有架构使用同一个文件。唯一的区别是指向配置文件位置和验证它的架构的两个条目的位置。这提供了一定程度的灵活性,因为系统可以共享相同的配置,或者如果您想将它们区分开来,可以指向不同的位置。
- 强类型分层数据
- 问题:虽然您可以使用
System.Configuration
创建层次结构,但当您实际访问属性时,仍然必须使用AppSettings["LooselyCoupledKey"]
。这不允许在编译时检测拼写错误,只能在运行时检测。 - 解决方案:SysConfiguration 生成一个表示配置类的分层对象模型。这意味着您可以获得易于理解、在编译时捕获并可以使用 IntelliSense 的代码(例如:
SysConfiguration.Data.AppConfig.ConnectionStrings.Core
)。 - 标准化配置条目
- 问题:在 T4G,我们倾向于尽可能共享组件。如果您想将一个组件传递给另一个人,那么可靠地传递该组件特有的配置条目将是理想的。如果您使用像 App.Config 这样的东西,您所能做的就是希望有人注释得足够好,以表明哪些设置属于哪个组件。
- 解决方案:SysConfiguration 的设计方式是配置的分层结构可以分解为许多文件。例如,它们可以分解为:AppConfig,存储当前应用程序特有的设置;T4GToolbox,存储我们可重用框架的所有设置;以及 RightsConfig,存储可重用安全组件的设置等。SysConfiguration 旨在将所有这些定义合并到一个配置文件中,因此每个环境只需维护一个文件。
灵活的单例模式
存储配置数据的理想设计模式是单例模式。您希望配置数据从磁盘加载一次,然后存储在快速的地方,以便可以快速访问。单例模式的问题是几乎所有示例都只是将其保存在内存中。如果这在 Web 上下文中使用,每次应用程序池或进程回收内存时,内存都会被清除。如果它用于 COM+ 组件中,则在闲置一段时间后会立即释放该内存。
SysConfiguration 检测其运行上下文,然后将其存储在适当的存储中
- 对于 Web 应用程序和 Web 服务,它使用 Web 缓存。
- 对于 WinForms 和控制台应用程序,它使用内存。
- 对于 COM+,它使用共享属性管理器。
测试驱动开发
我还想提一下,这个项目完全是使用测试驱动开发(TDD)构建的。虽然我可能没有尽可能严格地遵循“你不需要它”(YAGNI)的原则,但我认为这是优化和未来使用的正确平衡。虽然我以前曾将 TDD 用于组件,但我将这个项目用作测试平台,以验证 TDD 支持者提出的一些主张是否属实。请参阅“关于测试驱动开发的结论”。
就我遵循的过程而言,我始终先编写测试,使其失败(红色),然后使代码根据测试工作(绿色),重构单元测试,然后重新开始这个循环。
我包含了所有单元测试,这可能很有趣,因为我觉得我在跨所有架构测试单个组件时建立了一个相当好的流程。最重要的设计决策是,有一组由 NUnit 调用的测试,但在许多情况下,这些测试会调用驻留在不同进程中的其他测试。因此,有一个 NUnit 调用的中心程序集,但实际的单元测试逻辑存储在其他地方。这确保我可以随时一次性调用所有单元测试。
Using the Code
安装包括可选步骤,具体取决于您的目标。第一组步骤适用于对配置组件感兴趣的人。第二组步骤是附加步骤,如果您想了解更多关于测试驱动开发的信息。
我应该提一下,设置此项需要一些手动步骤,但一旦集成,维护起来就非常容易。
SysConfiguration 的先决条件
- 该项目是使用 .NET Framework 2.0 和 Visual Studio 2005 构建的。
- 您需要安装 XSDObjectGen (http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&displaylang=en)。这用于生成由架构生成的配置类。它假定使用默认安装路径,这将把 VS2005 版本安装到 C:\Program Files\XSDObjectGenerator\vs2005。
使用单元测试的附加步骤(可选)
如果您有兴趣执行单元测试,还有几个额外的步骤。
- 安装最新版本的 NUnit。在撰写本文时,版本为 2.4 (http://www.nunit.org/)。
- 下载 NUnitASP (http://nunitasp.sourceforge.net/)。在撰写本文时,版本为 1.5.1。将 zip 文件解压到 C:\Program Files\NUnitASP。
- 从 NUnitASP 的 bin 目录安装 NUnit 2.2。您将能够并行运行两个 NUnit。
- 我不得不将
<supportedRuntime version="v2.0.50727" />
添加到 nunit.exe.config 和 nunit-console.exe.config。您可能需要根据机器上安装的 .NET 运行时执行相同的操作。 - 您需要运行支持组件服务或 COM+ 的操作系统。有一个类,其测试是使用 regsvcs.exe 命令行工具安装的。
- UnitTestCommon\Constants.cs 中有一些常量需要更改。这些常量设置为它期望返回的路径,这可能与您的文件系统不同。您应该能够在第一次运行单元测试并失败时找出这些常量。
- 打开 UnitTests 项目的属性。转到调试。选择启动操作为“启动外部程序”。选择最新版本的 nunit.exe。
- 打开 WebTestHarness 项目的属性。转到 Web。选择“不打开页面。等待外部应用程序的请求。”
- 打开解决方案属性。转到启动项目,选择“多个启动项目”,然后选择“UnitTests”和“WebTestHarness”都启动。
添加 SysConfiguration
- 该项目并非设计为独立组件。有一个类是每次构建项目时根据架构文件自动生成的。然后,此文件与项目的其余部分一起编译。因此,需要将其添加到应用程序的解决方案中。
- 如果您不关心单元测试,请将配置项目复制到您的解决方案中。
- 如果您想要单元测试,请将配置项目复制到您的主解决方案中。将所有其他项目复制到您保存单元测试的任何位置。在我的例子中,我将它们保存在解决方案中的一个文件夹中,以便有明确的分离。请参阅 SysConfigurationWithUnitTests.sln 以获取示例。
- 您将需要配置您的应用程序以查找配置文件和验证它的架构。
- 如果是 COM+ 组件,SysConfiguration 需要使用注册表来查找路径。提供了一个示例 .reg 文件 SetupComPlus.reg。它将在 HKEY_LOCAL_MACHINE\SOFTWARE\Name.Randar 键中放置两个键(ConfigurationPath 和 ConfigurationSchemaPath)。
- 对于任何基于 Web 的应用程序,将以下内容添加到 Web.config 并将路径指向正确的位置。对于 .NET 应用程序,进行相同的更改,但添加到 App.config。
- 如果有依赖架构,您将不得不更改 Settings.xsd 中
xs:include
的绝对路径。似乎没有办法使路径相对,这很不幸。我建议您尝试在不同的环境(开发、预发布等)中保持相同的磁盘文件结构,或者确保在更新代码时不要覆盖您的 Settings.xsd。
<?xml version="1.0" ?>
<configuration>
<!-- Add these items to your configuration file and point them
at the location that has the configuration and schema-->
<appSettings>
<!-- Development Configuration Path -->
<add key="ConfigurationPath"
value="C:\Projects\SysConfiguration\Code\Configuration\
Development\Configuration.xml" />
<!-- Production Configuration Path -->
<!-- <add key="ConfigurationPath"
value="C:\Projects\SysConfiguration\Code\Configuration\
Development\Configuration.xml" /> -->
<!-- Path to Schema used to validate configuration files-->
<add key="ConfigurationSchemaPath"
value="C:\Projects\SysConfiguration\Code\
Configuration\Schemas\Settings.xsd" />
</appSettings>
</configuration>
SysConfiguration 任务
以下是使用 SysConfiguration 需要执行的常见任务的“帮助文件”。
添加配置元素
以下描述了在完成先决条件步骤后向应用程序添加新配置设置的步骤。SysConfiguration 旨在允许开发人员快速添加新配置设置并允许他们定义所需的验证。
- 如果您想添加配置元素,只需更改其中一个架构(例如,AppConfig.xsd、RightsConfig.xsd 等)中的定义。不要将配置元素添加到 Settings.xsd。这是各种配置定义的中心枢纽。它将生成一个包含所有其他配置文件所有引用的类。其概念类似于项目文件。
- 以下是一些受支持的示例
- 数据类型。如果您设置
xs:element
的类型,它将创建一个有效的 .NET 类型。 minOccurs
和maxOccurs
。如果maxOccurs
是无限制的,它们允许您创建集合;如果minOccurs
是 0,则允许您创建可选设置。
- 数据类型。如果您设置
添加配置数据
定义结构后,以下是添加配置数据的步骤
- 在您喜欢的 XML 编辑器中打开 Configuration.xml。如果您使用的是 Visual Studio,它应该已设置
targetNamespace
,以便 Intellisense 将允许您构建层次结构并添加数据。
访问配置元素
定义配置定义并添加数据后,以下描述了应用程序如何访问该数据
- 添加对 Name.Randar.Configuration.dll 的引用。
- 为命名空间添加一个
using
语句(例如:using Name.Randar.Configuration
)。 - 访问配置层次结构,从名为 SysConfiguration 的单例开始。您可以通过静态
Data
或Instance.SettingsData
访问数据。两者都是相同的。一些示例 SysConfiguration.Data.AppConfig.ConnectionStrings.Core
SysConfiguration.Instance.SettingsData. AppConfig.ConnectionStrings.Core
SysConfiguration.Data.DebugSettings.LogToFile
SysConfiguration.Instance.SettingsData.AppConfig.NumberOfRetries
SysConfiguration.Data.AppConfig.CloseOutDate
SysConfiguration.Instance.SettingsData.AppConfig.MaxAllowed
访问配置元素列表
SysConfiguration 支持单名称/值对以及列表。以下描述了如何访问数据列表
- 它将列表存储为
ArrayList
类的强类型子类。因此,您可以通过索引或遍历列表来访问它 SysConfiguration.Data.RightsConfig.RightCollection[0].Name
SysConfiguration.Data.RightsConfig.RightCollection.Count
// Search through the roles until you find the one we are looking
int findRole = -1;
for (int i = 0; i < settings.RightsConfig.RightCollection.Count; i++ )
{
if (settings.RightsConfig.RightCollection[i].Name == Constants.RoleToSearchFor)
findRole = i;
}
添加配置定义
如果您想扩展配置定义,但保留主要定义不变,您可以使用 xs:include
来包含其他架构。这允许您根据组件或其他要求分解配置定义。
- 创建定义您的配置的架构。确保它有一个根元素。
- 修改 Settings.xsd
- 添加一个
xs:include
并将其指向新文件。 - 创建一个引用其他架构文件的基本元素的元素。
<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="Settings"
targetNamespace="http://Randar.Name/Settings.xsd"
elementFormDefault="qualified"
xmlns="http://Randar.Name/Settings.xsd"
xmlns:mstns="http://Randar.Name/Settings.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:include schemaLocation=
"C:\Projects\SysConfiguration\Code\Configuration\Schemas\AppConfig.xsd">
</xs:include>
<xs:include schemaLocation=
"C:\Projects\SysConfiguration\Code\Configuration\Schemas\RightsConfig.xsd">
</xs:include>
<xs:element name="Settings">
<xs:complexType>
<xs:sequence>
<xs:element ref="AppConfig">
</xs:element>
<xs:element ref="RightsConfig">
</xs:element>
<!-- Add new elements here that reference other schema files-->
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
重新加载配置数据
您应该设置您的应用程序,以便有一种方法可以清除缓存并强制它在下次使用时重新加载数据。否则,您将不得不以某种方式重新启动进程以清除数据。以下步骤描述了如何做到这一点
- 您将不得不创建可以手动调用的东西。例如
- 管理区域中有一个按钮名为“清除缓存”的网页。
- 一个带有名为“
ClearCache
”的 Web 方法的 Web 服务。 - 帮助->关于屏幕上有一个按钮“重新加载配置”。
- 在所有这些情况下,该方法都应调用
SysConfiguration.ClearCache()
。
超出范围
以下场景尚未测试或未完全支持
- 向后兼容性。我已清除开发机器上的所有旧开发环境,包括 VB6。因此,我尚未在 .NET 2.0 Framework 之前的任何版本上测试过它。
- 我能够使用 Regasm.exe 注册组件,并在 OLE Viewer 中查看它,但没有任何类有方法。我自 2002 年以来就没有需要进行 COM\.NET 集成,所以我确定我忘记了一些小东西,但觉得这不是优先事项。
- 如果您需要将其移植到 1.0 框架
- 您将不得不使用较旧的解决方案文件,或者使用文本编辑器手动编辑解决方案。
- 将 SysConfiguration 折叠成一个类,而不是使用
partial
类。 - 使用旧版类而不是 2.0
System.Configuration
命名空间。 - 继承。不幸的是,如果不进行某种破解,例如基类知道超类,很难使单例设计模式可继承。这很不幸,因为我可以想到许多场景,其中
SysConfiguration
可以是一个很好的基类。我对此的最佳折衷方案是使用partial
类来分解单例和配置功能。 - 仅限 C#。构建一次已经足够费劲了。我没有时间同时用 VB.NET 创建它。
- 集中式配置数据。一些第三方配置管理器有一个集中式位置来存储配置数据,所有本地和远程进程都访问该位置。我从不喜欢这种架构,因为它有太多的跨进程/网络调用。如果您确实需要这个,您可以将其加载到组件服务中并通过它进行所有调用,但这会影响性能。
- 没有用于编辑配置的用户界面。我发现企业库提供的用户界面价值不大,我通常使用记事本编辑我的配置。请记住,
SysConfiguration
使用标准 XML,根据架构进行验证,因此您可以使用 Visual Studio 中的 XML 编辑器或任何其他第三方工具。由于 XML 是根据架构进行验证的,如果您输入无效值,应该会收到警告。 - 配置结构更改需要重新编译。正如我之前提到的,您不能在不重新编译的情况下更改配置的结构。这是因为该项目具有强类型特性。我不认为这是一个缺陷,因为您无论如何都必须更改代码才能访问新的配置元素,但我想提一下。显然,您可以在不重新编译的情况下更改配置数据,但不能更改结构。
- 编辑配置。没有内置支持用于编辑配置数据。可能有这样做的原因,但我从未遇到过。如果数据是易失的,我总是将其存储在数据库中,以便在服务器场中共享。归根结底,它只是 XML,所以您可以使用标准 XML 类对其进行编辑。
- 没有集成加密。.NET 2.0 具有加密配置文件部分的能力。我尚未内置此功能,但可以看到自己稍后添加它。目前,请使用标准密码学库自行完成。
- 不符合 CLS。虽然我的代码符合 CLS,但 XSDObjectGen 生成的代码不符合。生成的代码是一个关键项,我认为最好将整个类标记为不符合 CLS。
- 自动文件更改检测。App.config 和 Web.config 的一个不错(或不好)的功能是,如果配置文件更改,它将重新启动进程,重新加载设置。我尚未研究复制此功能(或缺陷,取决于您的观点)。我个人认为我更喜欢能够手动强制重新加载,但希望将其作为一个可选功能。
关注点
以下是一些关于代码和开发过程的评论,可能对某些人感兴趣。
关于测试驱动开发的结论
以下部分描述了我对测试驱动开发与传统代码开发过程(以及可能创建自动化单元测试)的看法。它不基于任何实证研究,应被视为观点,而非事实。
我绝对觉得 TDD 帮助我构建了一个比传统开发更健壮的组件。它非常适合快速重复测试各种架构。有几次,我对架构进行了重大重构,而拥有完整的回归测试对于帮助我使组件再次正常运行至关重要。
至于我是否使用 TDD 比传统开发方法更快地开发了这个组件,我不太确定。创建和重构单元测试确实需要时间,不能被忽视。论点是创建测试比调试问题花费的时间更少。准确测试此问题的唯一方法是克隆一个人,让他们在相同条件下但使用两种技术开发组件,这根本不可能。我确实认为,当团队人数超过一个人时,您会看到更大的回报,因为在这种情况下,另一个开发人员破坏他人代码的可能性要大得多。
一些 TDD 倡导者会说,当测试意外失败时,将代码恢复到通过所有测试的最新版本几乎总是比调试更有效。我不得不说,我确实比平时花在调试上的时间少得多,但我觉得我永远不应该回滚。我认为调试始终是更好的选择,特别是考虑到 VS2005 中调试器的出色程度。
尽管这个版本是由一个团队开发的,但我试着像在团队中工作一样。我努力确保只有在底层代码通过(即绿色)时才签入测试。理论上,这意味着如果我们使用持续集成,它将始终通过。唯一的问题是抵制一旦编写完测试就签入的冲动。我习惯于保持更改小而签入频繁的心态,但您必须稍微改变您的心态。
一个有趣的项目是单元测试代码行数与应用程序代码行数的比率。
- 总代码行数 4887 行
- 单元测试 3401 行
- 应用程序代码 1486 行
因此,我们最终得到单元测试与应用程序代码的大致比例为 2:1。这高于典型值,尽管许多测试是针对不同架构的重复测试,这可以解释高比例。
为了回答是否建议在项目中使用 TDD 的问题,答案是肯定的,但有一些注意事项。虽然您无法从这个项目中看出,但我建议将 TDD 用于由其他组件调用的任何组件。为 Web 层编写有效的单元测试仍然很困难,除非您以某种方式绕过身份验证和会话数据。我绝对认为它创建了更可靠的代码,并且由于它创建了广泛的回归测试,因此使更改更容易。
代码质量
由于这旨在供广大人员使用和扩展,我尝试
- 尽可能清晰地注释代码。
- 遵循 Lance Hunt 的 C# .NET 编码标准 (http://weblogs.asp.net/lhunt/pages/CSharp-Coding-Standards-document.aspx),这是 T4G 使用的标准。
- 使用 FxCop 清理一些异常处理和其他样式问题。许多排除项来自 XSDObjectGen 生成的代码,或者根本不适用于我试图实现的目标(例如,全球化)。
- 我真的很想使用 NCover 来确保我的单元测试有 100% 的代码覆盖率。这实际上比我预期的要难。许多测试在不同的进程(控制台、Web、COM+ 等)中运行。NCover 无法识别它们都来自同一组测试,因此它显示覆盖率很差,尽管所有测试的超集可能接近 100%。
XSDObjectGen.exe
我想对 XSDObjectGen,又名示例代码生成器 (http://www.microsoft.com/downloads/details.aspx?familyid=89e6b1e5-f66c-4a4d-933b-46222bb01eb0&displaylang=en) 做一些评论。
- 曾几何时,我尝试使用 XSD.exe 而不是 XSDObjectGen.exe 来生成我的类文件,但它有一个主要缺陷。不支持
<xs:import>
标签。这迫使您将所有定义都保存在一个文件中,这很难管理。 - 我发现 XSDObjectGen 比强类型数据集好得多,因为它们更轻巧,并且您可以创建比行和列更深的层次结构。唯一的问题是您必须自己加载数据。
- 如果您需要创建数据传输对象 (http://en.wikipedia.org/wiki/Data_Transfer_Object),您应该认真考虑 XSDObjectGen。它创建了出色的轻量级对象,可以很好地序列化。
故障排除
问题:系统引发 System.IO.FileNotFoundException
。
它找不到配置文件或用于验证它的架构。请参阅“添加 SysConfiguration”的步骤 2。
问题:编译器引发:“找不到路径 'C:\Projects\SysConfiguration\Code\Configuration\Schemas\RightsConfig.xsd' 的一部分。C:\Projects\SysConfiguration\Code\Configuration\Schemas\Settings.xsd。”
xs:import
似乎需要绝对路径。编辑 Settings.xsd 以指向正确的位置。
问题:编译器引发:“无法将文件 'obj\Debug (With Sample Configuration)\Name.Randar.txUnitTests.dll' 复制到 'bin\DebugWSC\Name.Randar.txUnitTests.dll'。进程无法访问文件 'bin\DebugWSC\Name.Randar.txUnitTests.dll',因为它正被另一个进程使用。”
COM+ 组件仍在运行。打开组件服务,展开 COM+ 应用程序,右键单击 SysConfiguration 单元测试,然后选择“关闭”。
结论
我在 2002 年编写了这个组件的第一个版本。从那时起,它被重建和重构了许多次。同时,整体核心设计变化很小,并且经受住了时间的考验。这个最新版本和附带的文章花了很多时间编写。您可以随意将其合并到您的代码中,我只要求您不要盗用我的作品,如果您使用它,请给我发送电子邮件提供反馈(无论是好的还是坏的)。
历史
- 2007 年 7 月 12 日:首次修订。
- 2007 年 8 月 8 日:删除了用于对程序集进行签名的强名称密钥上的密码。