创建基于二进制的组件
随着类的数量增加,创建二进制的组件就变得很平常了:你想分离部分功能。所有不同的是,二进制组件可以让你独立的保存这些离散的功能。创建的组件程序集可以方便的共享逻辑,方便交叉语言
编程,以及方便布置。
在.Net程序就是组件包,每一个程序什么样可以随时更新和发布。你应该已经知道,基于程序集之间的应用程序是多么容易更新呀,程序集之间的偶合是多么好呀!
最小偶合就是说更多的是减少了程序集之间复杂的依懒关系。同时也意味着你可以简单的更新小块新
的程序集。这一章就是关于创建简单易用,容易布置,以及容易更新的程序集。
.Net环境中的应用程序可以由多样的二进制组件组成。随后,你可以独立的更新这些组件,也就是可以在一个多程序集应用程序中安装与更新一个程序集。但你必须明白一件事,那就是CLR是如何发现和载入这些程序集的。在你创建这些二进制组件时,你也就必须创建符合这些明确期望的组件。接下来就介绍这一概念。
CLR并不会在程序启动时加载全部的引用程序集。更合适的作法是当运行时须要程序的成员时,加载器才解决程序集的引用问题。这可能是调用一个方法或者访问数据,加载器先查找引用的程序集,然后加载它,然后JIT编译须要的IL。
当CLR须要加载一个程序集时,先是断定那个文件要加载。程序集的元数据包含了对所有其它程序集的引用记录,这个记录有强名称和弱名称之分。一个强名称(程序集)由四部分组成:程序名,版本号,语言属性(译注:这里的语言范指区域性特定的信息,如关联的语言、子语言、国家/地区、日历和区域性约定),以及公开密钥。如果程序集不是一个强名称程序,那么就只记录了程序集名。如果你的程序是一个强名称程序集,那么你的程序集就不太可能被一些恶意的组件(程序集)所取代。强程序集还可以让你用配置文件对组件的版本进行配置,是新的,还是先前的。
确定正确的程序集以后,CLR接来就断定这个程序集是否已经在当前的应用程序中加载。如果是,就用原来的。如果不是,CLR就继承查找程序集。如果程序是强名称的,CLR就先在全局程序缓存(GAC)中查找,如果不在GAC中,加载器就检测代码目录(codebase
directory,译注,这里只是译为代码目录,其实并不是源代码所在的目录)中的配置文件,如果当前代码目录存在,那就是唯一被搜索程的目录了。如果在代码目录中没有找到所要的程序集,那么加载就失败。
- 应用程序目录。也就是与主应用程序集同在的位置。
- 语言目录。这是一个在应用程序目录下的子目录,这个目录与当前语言名匹配。
程序集子目录。这是一个与程序集同名的子目录,这两个可以组合成这样:
[语言]/[程序集名]
私有的运行目录(binPath)。这是一个在应用程序配置文件中定义的私有目录。它同样可以和语言和程序集名组合:
[bin目录]/[程序集名], 或者[bin目录]/[语言], 还可以是:[bin目录]/[语言]/[程序集名]。
从这个讨论中你应该明白三个内容:第一,只有强名称程序集才能放到GAC中。其次,你可以通过配置文件来修改默认的行为,从而升级程序中个别的强名称程序集。第三,强名称程序集可以阻止对程序集的恶意篡改,从而提供更安全的应用程序。
了解CLR是如何加载程序集的,有利于在实际操作中考虑如何创建和更新组件。首先,你应该使用强名称程序集,把所有的元数据信息都记录下来。当你在VS.net中创建一个项目时,你应该把assemblyInfo.cs文件中的所有属性都填满,包括完整的版本号。这会让你在后面的升级中更简单的。VS.net在assemblyInfo.cs中创建三个不同的部份,首先就是信息报告:
[assembly: AssemblyTitle("My Assembly")]
[assembly: AssemblyDescription ("This is the sample assembly")]
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
#else
[assembly: AssemblyConfiguration("Release")]
#endif
[assembly: AssemblyCompany("My company")]
[assembly: AssemblyProduct("It is part of a product")]
[assembly: AssemblyCopyright("Insert legal text here.")]
[assembly: AssemblyTrademark("More legal text")]
[assembly: AssemblyCulture("en-US")]
最后一条,AssemblyCulture
只针对本地化程序集。如果你的程序不包含任何的本地化资源,就空着。否则就应该遵从RFC1766标准填写语言描述信息。
接下来的部份就是版本号,VS.net是这样记录的:
[assembly: AssemblyVersion("1.0.*")]
AssemblyVersion包含4个部份:主版本号.副版本号.编译号.修订号,星号就是告诉编译器用当前时间来填写编译号和修订号。编译号就是从2000年1月1号起的天数,而修订号则是从当天凌晨起的秒数除以2。这样的算法可以确保修订号是不断递增的:每次编译后的号码都会比前一次高。(译注:我有一点不明白的,就是如果我把本机时间修改了呢?或者两台机器上的时间不一致,会有什么问题呢?当然,这并不会有什么太大的问题。)
对于这一版本号的算法,好消息是两个编译的版本不会有完全相同的版本号。坏消息就是在你编译并发布后,要记住这个版本号。我个人比较喜欢让编译器给我生成编译和修订号。通过记录发布时的编译号,我就知道最后的版本号了。我从来不会忘记在我发布一个新程序集时修要改它的版本号。但也有例外,COM组件是在你每次编译后自动注册的。如果还让编译器自己生成编译号后然后注册它,那么很快就让注册表里填满了无用的信息。
最后一部份就是强名称信息:
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]
[assembly: AssemblyKeyName("")]
应该考虑为所有的程序集都创建强名称,强名称程序集可以防止别人的篡改,而且可以为单个应用程序独立更新个别程序集。然而,你应该在ASP.net应用程序中避免使用强程序集;局部安装的强名称程序集不会正确的加载。同样,强名称程序集必须用AllowPartiallyTrustedCallers特性进行修饰,否则它们不能被非强名称程序集访问(参见原则47)。
实际在更新一个组件时,公共的和受保护的接口部份必须在IL语言层上是兼容的。也就是说不能删除方法,不能修改参数,不能修改返回值。简单的说,没有一个组件愿意在引用你的组件后要重新编译。
你可以添加配置信息来修改引用的程序集。配置信息可以存储在不同的位置,这取决于你想如何更新组件。如果是一个简单的应用程序,你可以创建一个应用程序配置文件,然后把它存放在应用程序目录就行了。如果是配置所以使用同一个组件的应用程序,你可以在GAC中创建一个发布策略文件。最后,如果是全局的修改,你可以修改machine.config文件,这个文件在.Net进行时的配置目录里(参见原则37)。
实际操作中,你可能从来不用修改machine.config文件来更新你的程序集。这个文件包含了基于整台机器的信息。你可以通过应用程序配置文件来更新单个应用程序配置,或者使用一个发布策略来更新多个程序公用的组件。
应该考虑为所有的程序集都创建强名称,强名称程序集可以防止别人的篡改,而且可以为单个应用程序独立更新个别程序集。然而,你应该在ASP.net应用程序中避免使用强程序集;局部安装的强名称程序集不会正确的加载。同样,强名称程序集必须用AllowPartiallyTrustedCallers特性进行修饰,否则它们不能被非强名称程序集访问(参见原则47)。
实际在更新一个组件时,公共的和受保护的接口部份必须在IL语言层上是兼容的。也就是说不能删除方法,不能修改参数,不能修改返回值。简单的说,没有一个组件愿意在引用你的组件后要重新编译。
你可以添加配置信息来修改引用的程序集。配置信息可以存储在不同的位置,这取决于你想如何更新组件。如果是一个简单的应用程序,你可以创建一个应用程序配置文件,然后把它存放在应用程序目录就行了。如果是配置所以使用同一个组件的应用程序,你可以在GAC中创建一个发布策略文件。最后,如果是全局的修改,你可以修改machine.config文件,这个文件在.Net进行时的配置目录里(参见原则37)。
实际操作中,你可能从来不用修改machine.config文件来更新你的程序集。这个文件包含了基于整台机器的信息。你可以通过应用程序配置文件来更新单个应用程序配置,或者使用一个发布策略来更新多个程序公用的组件。
这是一个XML配置文件,它描述了存在的版本信息和升级后的信息:
<dependentAssembly>
<assemblyIdentity name="MyAssembly" publicKeyToken="a0231341ddcfe32b" culture="neutral" />
<bindingRedirect oldVersion="1.0.1444.20531" newVersion="1.1.1455.20221" />
</dependentAssembly>
你可以通过这个配置文件来标识程序集,旧版本,以及升级后的版本。当你安装和更新了程序集后,你就要更新或者创建一个恰当的配置文件,然后程序程序就可以使用新的版本了。
如果你的软件是一个程序集的集合:你希望个别的更新它们。通过一次更新一个程序集,你须要做一些预先的工作,那就是第一次安装时应该包含一些必要的支持升级的信息。