我们的应用程序发布后,常常因用户需求变更或者发现Bug而要求应用程序升级,.Net给我们提供了一个方便的方法升级独立模块(Dll或者exe)。如果升级后客户发现使用起来不方便还可以简单的退回到前一个版本。这个方法的叫做配置管理控制。
为了讲清楚这个问题我们会遇到以下几个概念:程序集,弱命名程序集,强命名程序集,私有方式部署,全局部署。
1、程序集,说白了就是基于.Net平台的一个 *.dll 或者 *.exe为后缀的文件。
程序集,包括以下部分:
PE32(+)头
CLR 文件首部
元数据
程序集清单表
IL
其中PE32(+)头是为了告诉Windows当前文件的文件类型,文件可执行版本(32或者64位),如果包含CPU则会含有本地CPU代码有关信息。
CLR文件首部:告诉Windows使用哪个版本的CLR
元数据:自描述信息,包含定义表(definitiontable)、引用表(reference table)和清单表(manifest table)用于反射等。
程序集清单:程序集自描述信息,用于相关程序集加载和反射等。
IL:中间代码,所有的实现部分就在这里。
2、弱命名程序集与强命名程序集在结构上完全一样,使用相同的PE文件格式,包含PE32(+)头、CLR头、元数据、清单表、IL。区别是强命名程序集使用发布者的公钥/私钥对进行了签名,唯一性的标识了程序集的发布者。弱命名程序集只能以私有方式部署。
3、“全局部署的程序集”是指部署到一些已知位置的程序集(.Net3.5前的版本长部署到C:\Windows\Assembly。对于4.0位于C:\Windows\Microsoft.Net\Assembly);强命名程序集既可以是私有部署也可以是全局部署。私有方式部署只允许部署到可执行文件所在的基目录及子目录。
查看程序集方式多种,常用的一种是在“Visual studio 命令提示”中输入dump/headers 程序集名称,如图1所示。另外一种就是使用IL反汇编程序,如图2所示,快捷键ctrl+M可查看元数据。
图1使用dumpbin/headers命令查看DeployTest.exe
图2 使用IL反编译器查看DeployTest.exe
如果要实现私有部署应用程序能够升级,前提条件是应用程序引用的模块或者程序集是强命名程序集,因为CLR在加载弱命名程序集时根本不考虑版本号,因此无法通过版本标识而升级。
下面就让我们自己部署一个叫做DeployTest的应用程序,该应用程序调用了程序集SomeMethodLib,下面讲述SomeMethodLib如何由原始的1.0.0.1版本升级到1.0.0.3。
首先创建方法库SomeMethodLib,功能非常简单两个字段一个事件五个函数。如代码1所示。
代码1 SomeMethod.cs
public class SomeMethod { private int m_Num1; private int m_Num2; public event Action<int> Num2ChangedHandle; public int Num2 { get { return m_Num2; } set { OnNum2ChangedHandle(value); m_Num2 = value; } } public void ChangeNum1(int num) { m_Num1 = num; } public int GetMultipleOfNum2(int multiple) { return m_Num2 * multiple; } private double GetPowerOfNum2(int power) { return Math.Pow(m_Num2, power); } protected virtual void OnNum2ChangedHandle(int num) { Action<int> tmp = Interlocked.CompareExchange<Action<int>>(ref Num2ChangedHandle, null, null); if (tmp != null) { tmp.Invoke(num); } } public override string ToString() { return Assembly.GetExecutingAssembly().FullName; } }
可通过如图3所示设置程序集属性,如图4所示设置SomeMethodLib为强命名程序集。
图3设置程序集信息
图4 设置强命名程序集
为了便于说明,我们生成3个版本号,分别为1.0.0.1,1.0.0.2,1.0.0.3。分别放在Debug的三个目录下,如图5所示。三个目录中的文件都叫做“SomeMethodLib.dll”。
图5三个目录下的强命名程序集
再创建控制台应用程序的入口程序集叫做DeployTest,主函数是对SomeMethodLib.SomeMethod实例的调用,如代码2所示
代码2 DeployTest.Program
class Program { static void Main(string[] args) { SomeMethodLib.SomeMethod sm = new SomeMethodLib.SomeMethod(); sm.Num2 = 5; int valu = sm.GetMultipleOfNum2(5); Console.WriteLine(valu); Console.WriteLine(sm.ToString()); Console.Read(); }
注意,程序集DeployTest调用的SomeMethodLib版本号为1.0.0.1。我们要构造的应用程序目录结构如图6所示。
图6 应用程序的目录结构
这完全不同于VS默认的生成方式,目前应用程序还不能运行,基目录中出现一个叫DeployTest.exe.config的文件。那么文件“DeployTest.exe.config”有什么功能,应用程序如何才能运行呢?
DeployTest.exe.config是一个XML格式的配置文件,可通过如图7所示方式添加app.config文件,在生成应用程序时VS自动在应用程序基目录中生成这样一个文件:由应用程序主程序集文件名称附加一个.config扩展名的文件。
图7 添加应用程序配置文件
配置文件的内容如代码3所示
代码3 app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="NoSN;V1;V2;V3"/> </assemblyBinding> </runtime> </configuration>
如果调用弱命名程序集,CLR尝试定位一个程序集文件时,总是在基目录中查找,如果没有找到,就查找子目录NoSN;V1;V2;V3。不能指定一个绝对或者相对路径来表示在应用程序基目录外部的一个目录。
如果调用的是强命名程序集(如本例所示),CLR知道它应该加载的程序集版本,并尝试从GAC中加载该程序集,如果程序集不在GAC中,且在.config文件中没有CodeBase元素(下面会讲到),CLR会像调用弱命名程序集那样查找,如果执行最后一次重定向操作的配置文件同时包含一个CodeBase元素,CLR会尝试从CodeBase元素制定的URL处加载程序集。
生成的应用程序执行结果如图8所示,可以看出应用程序调用的是版本1.0.0.1。图8 第一次发布 应用程序执行结果
当SomeMethodLib升级为1.0.0.2后通过更改.congfig文件就可以让DeployTest程序集不做任何更改就调用新版本。更改的.config文件见代码4。
代码4 DeployTest.exe.config
<?xml version="1.0" encoding="utf-8" ?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="NoSN;V1;V2;V3"/> <!—确定依赖程序集,查找公钥为1217633aa9436597,语言文化为中性版本号为1.0.0.1的程序集SomeMethodLib重定向为版本号为1.0.0.2的相同名称程序集--> <dependentAssembly> <assemblyIdentity type="win32" name="SomeMethodLib" publicKeyToken="1217633aa9436597" culture="neutral"/> <bindingRedirect oldVersion="1.0.0.1" newVersion="1.0.0.2"/> <codeBase version="1.0.0.2" href="V2/SomeMethodLib.dll"/> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
运行结果见图9
图9 调用1.0.0.2的执行结果
要升级为1.0.0.3时只需要改动dependentAssembly的内容,见代码5。运行结果见图10。
代码5 .config部分片段
<dependentAssembly> <assemblyIdentity type="win32" name="SomeMethodLib" publicKeyToken="1217633aa9436597" culture="neutral"/> <bindingRedirect oldVersion="1.0.0.1-1.0.0.2" newVersion="1.0.0.3"/> <codeBase version="1.0.0.3" href="V3/SomeMethodLib.dll"/> /> /dependentAssembly>
图10 重定向为1.0.0.3的运行结果
至此,我们已经实现了通过更改.config文件实现了升级引用强命名程序集。应用程序管理员通过手工配置可升级或者降级部分功能。
有一种简单的方式,可以不经过用户操作而实现自动升级,那就是发布者策略控制。在生成SomeMethodLib文件的同时也生成SomeMethodLib.config文件。如果要使用发布者策略程序集需要使用命令工具AL.exe。最终得到两个文件SomeMethodLib、SomeMethodLib.config,必须安装到用户机器GAC中才能使用。
源代码下载