Effective C# 原则37:使用标准的配置机制

JerryXia 发表于 , 阅读 (909)

我们要寻求一种避免直接写代码的应用程序配置和信息设置方法,我们已经创建了多种不同的策略来存储配置信息。而我们是要寻求一种正确的方法,我们要不断提高和改我们的想法,关于哪里是放置这些信息的好地方。INI文件?这是Windows3.1做的事,配置信息的结构是受限制的,而且在文件名上可能还会与其它程序程序相冲突。注册表?是的,是这个正确的想法,但它也有它的限制。乱七八糟的程序可能会通过在注册表里写一些错误信息来严重破坏计算机。正因为写注册表存在危险,一个应用程序必须有管理员权限来写注册表的一部份。你的所有用户都会是以具有修改注册表权利的管理员身份在运行吗?希望不是,如果你使用注册表,而你的用户不是以管理员身份运行的,在试图读写注册表时,将会得到一个异常和错误。

谢天谢地,还有很多更好的方法来存储设置信息,这样你的程序可以根据用户的选择不同适应不同的行为,例如安装参数,机器设置,或者其它任何事情。.Net框架提供了一个标准的设置位置,这样你的程序可以使用它来存储配置信息。这些存储位置是由应用程序特别指定的,而且当程序执行的机器上的用户被限制了权限时一样可以有效的工作。

只读的信息是属于配置文件的,XML文件控制应用程序中不同类型的行为;定义的结构表指明了所有的元素和属性,而这些都是.NET
FCL从配置文件中分析出来的。
这些元素控制一些设置,例如正在使用那个框架版本,支持的调试级别(参见原则36),以及程序集的搜索路径。有一个节点你是必须要明白的,那就是appSettings部份,它可以同时应用与web应用程序和桌面应用程序。运行程序在启动时读取这一节点的信息,它加载所有的关键字和值到一个属于应用程序的名字值集合(NameValueCollection)中。这是你自己程序的一部份,你可以添加任何程序须要的值来控制程序行为。当修改配置文件时,也就修改了程序行为。

对于使用配置文件来说,ASP.Net应用程序比桌面应用程序的伸缩性稍灵活一点。每个个虚拟目录可以有一个自己的配置文件,这个文件被每个虚拟目录依次读取,而每个虚拟目录也就对应一个URL的一部分。The
most local wins.
例如,这个URL:http://localhost/MyApplication/SubDir1/SubDir2/file.aspx
可能被4个不同的配置文件所控制。machine.config最先读取,其次是在MyApplication中的web.config文件,接着是在SubDir1
和SubDir2中的web.config文件。而它们每一个都可以修改前一个配置文件设置的值,或者是添加自己键/值对。你可以通过这种配置继承方式,来配置一个全局应用程序的参数选择,而且可以限制一些私有资源的访问。web应用程序在不同的虚拟目录中有不同的配置。

在桌面应用程序中,对于每个应用程序域只有一个应用程序程序配置文件。.Net运行时在载入每个可执行文件时,为它创建一个默认的应用程序域,然后读取一个预先军定义的配置文件到这个应用程序域中。默认的配置文件在与应用程序运行时的同一个目录中,而且就以<应用程序名>.<扩展名>.config来命名的。例如:MyApp.exe可能就有一个名为MyApp.exe.config的配置文件。appsettings部份可以用于创建你自己的键/值对到应用程序中。

配置文件是存储一些控制程序行为的信息的最好的地方。但你可能很快会发现,应用程序没有API来写配置文件信息。配置文件不是用于存储任何有序设置的地方。不要急着写注册表,也不要自己乱写。这里有一个更好的方法让你配置桌面应用程序。

你可能须要定义配置文件的格式,而且把配置文件放到正确的地方。通过在全局设置上定义一些设置结构和添加公共的读写属性,你可以很简单的存储和取回这些设置:

[Serializable()]
public struct GlobalSettings
{
  // Add public properties to store.
}

XML序列化来存储你的设置:

XmlSerializer ser = new XmlSerializer(typeof( GlobalSettings ));
TextWriter wr = new StreamWriter( "data.xml" );
ser.Serialize( wr, myGlobalSettings );
wr.Close( );

使用XML格式就意味着你的设置可以很容易的阅读,很容易的解析,以及很容易的去调试。如果须要,你可以对这些用户设置进行加密存储。这只是一个使用XML序列化的例子,不是对象持久序列化(参见原则25)。XML序列化存储文件,不是整个对象树。配置设置以及用户设置一般不会包含网状对象,而且XML序列化是一个简单的文件格式。

最后一个问题就是,应该在哪里存储这些信息。你应该在三个不同的地方放置配置信息文件。选择哪一个要根据配置的使用情况:全局,单用户,或者单用户且单机器。这三个位置可以通过调用System.Environment.GetFolderPath()
而取得。你应该在GetFolderPath()返回的路径后添加上应用程序的详细目录。请格外小心的在所有用户或者机器范围上填写信息。这样做要在目标机器是取得一些特权。

Environment.SpecialFolder.CommonApplicationData返回存储信息的目录,这一目录是被机器上的所有用户所共享的。如果在一台机上使用的是默认安装,GetFolderPath(SpecialFolder.CommonApplicationData)会返回
C:\Documents and Settings\All Users\Application
Data。存储在这一目录的的设置应该是被机器上的所有用户所使用的。当你要在这里创建信息时,让安装程序给你做或者以管理员模式进行。不应该在这里写一些用户级(译注:users级是windows里的一个用户组,权利比管理员小。)的程序数据。偶然可能会让你的应用程序在用户机上没有足够的权限来访问。

Environment.SpecialFolders.ApplicationData返回当前用户的路径,而且在网络上被所有机器共享的。在默认安装中,GetFolderPath(SpecialFolders.ApplicationData)返回
C:\Documents and Settings\<用户名>\Application
Data。每个用户有他(或她)自己的应用程序数据目录。当用户登录到一个域是,使用这个列举进入到共享网络上,而且在网络上包含了用户的全局设置。存储在这里的数据只由当前用户使用,不管是从网络上的哪台机器登录过来的。

Environment.SpecialFolders.LocalApplicationData返回一个特殊的目录,该目录队了存储设置信息以外,同时也是一个用户的私人目录,它只属于从这台机器上登录的用户。一般GetFolderPath(SpecialFolders.LocalApplicationData)返回:C:\Documents
and Settings\<用户名>\Local Settings\Application Data

这三个不同的位置可以让你存储每个人的设置信息,给定用户的信息,或者是给定用户并给定机器的信息。具体的使用哪一个取决于应用程序。但考虑一些明显的例子:数据库链接字符串是一个全局设置,它应该存在在通用应用程序数据(Common
Application Data)
目录中。一个用户的工作内容应该存在在应用程序数据(Application
Data)目录中,因为它只取决于用户。窗口的位置信息应该在本地应用程序数据(Local
Application
Data)目录中。因为它们取决于机器上的用户的属性(不的机器可能有不同的分辨率)。

应该有一个特殊的目录,它为所有应用程序的所有用户设置存储,描述顶层的目录结构。这里,你须要在顶层目录结构下创建子目录。.Net框架的System.Windows.Application类定义了一些属性,这些属性可以为你创建一些通用的配置路径。Application.LocalAppDataPath属性返回GetFolderPath(SpecialFolders.CommonApplicationData)+"\\CompanyName\\ProductName\\ProductVersion"的路径。类似的,Application.UserDataPath和Application.LocalUserDataPath产生位于用户数据和本地数据目录下的路径名,这一目录包括公司,应用程序,以及版本号。如果你组合这些位置,你就可以为你自己公司的所有应用程序创建一个配置信息,或者为某一程序的所有版本,或者是特殊版本。

注意到了,这些目录中我没有提到过应用程序目录,就是在Program
Files下的目录。你决不应该在Program Files或者在Windows
系统目录以及子目录里写数据。这些目录要更高的特权,因此你并不能指望你的用户有权利来写这些数据。
当在哪里存储应用程序的设置数据成为一个很重要的问题,就像每个企业级用户到家庭用户所担心的机器安全问题一样时,把信息放在正确的位置就意味着对于使用你的应用程序的用户来说没有折衷的办法。你还是要给用户提供私人的感觉,用.Net的顺序组合正确的位置,这样可以很容易的给每个用户一种私有的感觉,而且不用折衷安全问题。

添加新评论