zoukankan      html  css  js  c++  java
  • Effective C# 第4章:创建基于二进制的组件(译)

    Effective C# 第4章:创建基于二进制的组件 
    Chapter 4. Creating Binary Components

    随着类的数量增加,创建二进制的组件就变得很平常了:你想分离部分功能。所有不同的是,二进制组件可以让你独立的保存这些离散的功能。创建的组件程序集可以方便的共享逻辑,方便交叉语言 编程,以及方便布置。

    在.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文件来更新你的程序集。这个文件包含了基于整台机器的信息。你可以通过应用程序配置文件来更新单个应用程序配置,或者使用一个发布策略来更新多个程序公用的组件。

    这是一个XML配置文件,它描述了存在的版本信息和升级后的信息:

    <dependentAssembly>
      <assemblyIdentity name="MyAssembly"
        publicKeyToken="a0231341ddcfe32b" culture="neutral" />
      <bindingRedirect oldVersion="1.0.1444.20531"
        newVersion="1.1.1455.20221" />
    </dependentAssembly>

    你可以通过这个配置文件来标识程序集,旧版本,以及升级后的版本。当你安装和更新了程序集后,你就要更新或者创建一个恰当的配置文件,然后程序程序就可以使用新的版本了。

    如果你的软件是一个程序集的集合:你希望个别的更新它们。通过一次更新一个程序集,你须要做一些预先的工作,那就是第一次安装时应该包含一些必要的支持升级的信息。
     
    ==========================
       

    Chapter 4. Creating Binary Components
    Creating binary components has much in common with creating classes: You are trying to separate and partition functionality. The difference is that binary components enable you to deliver those discrete bits of functionality separately. You create component assemblies to simplify sharing logic, facilitate cross-language programming, and simplify deployment.

    Assemblies are component packages in .NET. Each assembly can be released and upgraded independently. How easily you can upgrade a deployed application on an assembly-by-assembly basis depends on how well you minimize the coupling between assemblies. Minimizing coupling means more than just minimizing the compilation dependencies between assemblies. It also means building assemblies that you can easily upgrade in the field with new versions. This chapter is about creating assemblies that are easy to use, easy to deploy, and easy to upgrade.

    The .NET environment is designed to support applications that consist of multiple binary components. Over time, you can upgrade those components independently, installing and upgrading one assembly in a multiassembly application. You must have some understanding of how the CLR finds and loads assemblies to utilize this feature. You also must create components that conform to certain expectations if you are to make the most of this binary upgrade. The rest of this introduction explains these concepts.

    The CLR loader does not load every referenced assembly when your program starts. Rather, the loader resolves assembly references when the runtime needs members in that assembly. This can be a method call or a data access request. The loader finds the referenced assembly, loads it, and JITs the necessary IL.

    When the CLR needs to load an assembly, the first step is to determine what file gets loaded. The metadata for your assembly includes a record for each of the assemblies it references. This record is different for strong- and weak-named assemblies. A strong name consists of four items: the text name of the assembly, the version number, the culture attribute, and the public key token. If the requested assembly does not have a strong name, only the assembly name is part of the record. If you use strong names, your components will be less likely to be replaced by malicious components. Strong names also enable you to use config files to map the requested version to a new, improved version of a component.

    After determining the correct assembly and version, the CLR determines whether the assembly is already loaded into the current application context. If so, that assembly is used. If not, the CLR continues by looking for the assembly. If the requested assembly has a strong name, the CLR first searches the Global Assembly Cache (GAC). If the requested assembly is not in the GAC, the loader checks for a codebase directory in the configuration files. If a codebase directory exists, only that directory is searched for the requested assembly., If the requested assembly is not found in the codebase directory, the load fails.

    If there is no codebase directive, the loader searches a set of predefined directories:

    The application directory. This is the same location as the main application assembly.

    The culture directory. This is a subdirectory under the application directory. The subdirectory name matches the current culture.

    The assembly subdirectory. This subdirectory name matches the requested assembly name. These two can be combined in the form of [culture]/[assemblyname].

    The private binpath. This is a private directory defined in the application config file. This can also be combined with the culture and assembly path: [binpath]/[assemblyname], or [binpath]/[culture], or even [binpath]/[culture]/[assemblyname].

    You should remember three items from this discussion. First, only strong-named assemblies can be stored in the GAC. Second, you can use configuration files to modify the default behavior to upgrade individual strong-named assemblies in your application. Third, strong-named assemblies provide greater application security by preventing malicious tampering.

    This introduction into how the CLR loads assemblies should get you thinking about creating components that can be updated in the field. First, you should consider creating strong-named assemblies, with all the metadata records filled in. When you create projects in VS .NET, you should fill in all the attributes created in assemblyInfo.cs, including a complete version number. This makes it easier to upgrade the component in the field later. VS .NET creates three different sections in assemblyInfo.cs. The first is mostly informational:

    [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")]

    The last item, AssemblyCulture is filled in only for a localized assembly. If your assembly does not contain any localized resources, leave it blank. The string describing the culture conforms to the RFC 1766 standard.

    The next section contains the version number. VS .NET writes this:

    [assembly: AssemblyVersion("1.0.*")]

    AssemblyVersion contains four portions: Major.Minor.Build.Revision. The asterisk tells the compiler to fill in the build and revision using the current date and time. The build number contains the number of days since January 1, 2000. The revision contains the number of seconds since midnight local time, divided by 2. This algorithm guarantees that the build and revision numbers continue to grow: Each new build has a greater number than before.

    The good news about this algorithm is that no two builds have exactly the same version. The bad news is that you need to record the build and revision numbers after the fact for the builds that do get released. My preference is to let the compiler generate the build and the revision numbers for me. By recording the exact build number generated when I release the assembly, I know the final version number. I never forget to change versions when I release a new assembly. There are exceptions, though. COM components register themselves every time you build them. Letting the compiler automatically generate build numbers creates new Registry entries every build, which quickly fills the Registry with useless information.

    The last section contains information about strong names:

    [assembly: AssemblyDelaySign(false)]
    [assembly: AssemblyKeyFile("")]
    [assembly: AssemblyKeyName("")]

    Consider generating a strong name for all your assemblies. Strong-named assemblies are safe from tampering and provide the support for upgrading independently of the other assemblies in an application. However, you should avoid strong names on ASP.NET applications; locally installed strong-named assemblies don't load properly. Also, strong-named assemblies must be decorated with the AllowPartiallyTrustedCallers attribute, or they cannot be accessed by non-strongly-named assemblies (see Item 47).

    To upgrade a component in the field, the public and protected portions of the interface must be compatible at the IL level. That means no deleted methods, no modified parameters, and no changed return values. In short, none of the components that reference your component would need a recompile.

    You add configuration information to change the referenced assembly. The configuration information can be stored in three different locations, depending on the component you want to upgrade. To configure a single application, you create an application config file, located in the same directory as the application. To configure all applications that use a single component, you create a publisher policy file in the GAC. Finally, to make a global configuration change, you can edit the machine.config file, located in the Config directory under the .NET runtime (see Item 37).

    In practice, you will never modify the machine.config file toupgrade your assemblies. This file contains information on a machine-wide basis. You will use the application config file to upgrade one application and then use a publisher policy file to upgrade a component shared by many applications.

    The config files contain XML that describes the existing version and the upgraded version:

    <dependentAssembly>
      <assemblyIdentity name="MyAssembly"
        publicKeyToken="a0231341ddcfe32b" culture="neutral" />
      <bindingRedirect oldVersion="1.0.1444.20531"
        newVersion="1.1.1455.20221" />
    </dependentAssembly>

    You use the config file to identify the assembly, the old version, and its upgraded version. When you install an upgraded assembly, you update or create the appropriate config file, and the applications use the new version.

    Think of your software as a collection of assemblies: You want to be able to upgrade them independently. To update software by updating one assembly at a time, you need to do some upfront work so that your first installation contains the necessary information to support upgrades.
     

  • 相关阅读:
    做了半年的答题小程序上线了
    党建答题助手小程序
    党建答题活动小程序
    微信答题活动小程序
    微信答题活动小程序
    如何搭建在线考试小程序
    如何搭建在线考试小程序
    基于云开发的在线答题小程序
    XLua访问C#中的List或者数组
    字符串、字节数组、流之间的相互转换以及文件MD5的计算
  • 原文地址:https://www.cnblogs.com/WuCountry/p/681636.html
Copyright © 2011-2022 走看看