Versioning

Team-Fly    

 
.NET and COM Interoperability Handbook, The
By Alan Gordon
Table of Contents
Chapter Two.  Comparing COM and .NET


Any environment that uses binary software components must address the software component versioning problem. This problem occurs when you install an application, and suddenly some other application stops working. The culprit is usually DLLs that are shared by several applications. The MFC runtime DLL, mfc42.dll, is a good example of a shared DLL. It is used by all applications that are built with Visual C++. If you install an application that was built with Visual C++ (and therefore depends on mfc42.dll) and then later install some other application that uses an earlier version of mfc42.dll, the first application will likely cease to work if the second application installs the older version of the DLL. If the second application senses that a newer DLL is already resident on the system and does not install the older version (a correctly written setup program should do this), it is possible that the second application will not work. The second application is now using a different version of the MFC runtime DLL than it was built with. Usually, newer DLLs are backward compatible with old ones, but there are no guarantees . So how do you fix this problem?

COM's solution was immutable interfaces. The basic idea is that, after you published an interface in a COM class, you should never change that interface. If you want to add new functionality to a component, you should add additional interfaces to the component, preserving the older interfaces unchanged. This shouldin theoryguarantee that client software that was built using a particular interface will always work. My own experience is that the COM solution worked better than most people gave it credit for. Most of the versioning problems that I encountered weren't caused by COM components. When these problems were caused by COM components, it was because developers were not following the rules. There were some problems with the COM approach, though.

One problem is that it is possible for the behavior of a method to change even though the signature of the method (the number and type of its parameters and its return value) is the same. Perhaps using zero for the first parameter means one thing for version 1 and something totally different for version 2. Indeed, the COM specification said that new versions of software components should preserve both the signature and the semantics of its interfaces, but in practice it was easy for subtle behavior changes to creep in, especially over many new versions. Speaking of many new versions, another problem with the COM approach to versioning is the sheer proliferation of interfaces that tend to occur as you maintain and enhance a component over many years. For instance, in my COM class that I taught at UCLA, I used the Microsoft Active Movie Control for an in-class exercise on building applications using ActiveX controls. It made for a very visual and fun exercise. I noticed as the years went by the proliferation of IActiveMovie interfaces in the ActiveX control. By the time I stopped teaching the class, after 3 years, there were three IActiveMovie interfaces: IActiveMovie, IActiveMovie2, and IActiveMovie3. Each of these interfaces was only slightly different from the others, but, as Microsoft added new features to the Active Movie control, it was forced to add new interfaces in order to obey COM's immutable interfaces rule. Therefore, although it is possible to maintain backward compatibility from version to version with COM, in practice, it was difficult and a lot of work.

.NET takes a completely different approach to versioning. The solution can really be summed up with three concepts: private assemblies, metadata, and side-by-side execution.

Private Assemblies

A private assembly exists (on a particular machine) solely for the use of a single application. That assembly will not be shared by any other applications on the machine. Microsoft recommends that you use private assemblies as much as possible. Each application should be distributed with the versions of the assemblies that it was built and tested , with and those assemblies should be deployed into a directory that is private to that application. Obviously, this fixes the notorious versioning problems (DLL hell) that COM and regular Win32 DLLs were always afflicted with. If an application does not share its assemblies with another application, there is no danger that one application will install a version of an assembly that will break another application on the same machine. The only down side to this approach is that you will use up more disk space. If you have a dozen applications on your machine that depend on a particular assembly there will be a dozen potentially different versions of the assembly resident on your hard drive if you use private assemblies,. However, nowadays this isn't a problem; hard disk space and RAM are cheap. At the time when Win32 and COM were architected, both RAM and hard disk space were far more scarce and expensive than they are now. It is far cheaper to buy a larger hard disk so that each application can use the version of the assembly that it was built and tested with than it is to have a highly paid system administrator or developer tracking down a problem that was caused by an incompatible version of a shared DLL.

Metadata

Although Microsoft recommends that you use private assemblies, obviously, they are not suitable in all situations. The assemblies that comprise the .NET Framework class library are a good example of assemblies that must be shared by many applications. The .NET Framework class library is therefore implemented as a set of shared assemblies that are deployed in the GAC. There will be situations where you will need to create shared assemblies also. Thankfully, even in this situation, .NET provides technologies that should improve the versioning problem. Every .NET assembly stores not just metadata describing the types that are implemented within the assembly, but they also store copious metadata describing all of the assemblies that the assembly is dependent on. This metadata stores the name of each dependent assembly. This name includes a cryptographic key, if the assembly has a strong name , culture information, and, most important (at least for this discussion), the version # of the dependent assembly. This version number contains four parts .

 [major version].[minor version].[build number].[revision] e.g. 1.2.52.123 

Let's say I have assembly #1, which uses types from assembly # 2. When developers build assembly #1, they will reference assembly #2, and the metadata for assembly #1 will contain, the name, the version #, culture information, and the public key of assembly #2 as shown in Figure 2-12.

Figure 2-12. An assembly reference.

graphics/02fig12.gif

When code that resides in assembly #1 first uses a type from assembly #2, the CLR will attempt to locate assembly #2. By default, the CLR will search for the same version of assembly #2 that assembly #1 was built with. In Chapter 3, you will see that there are a number of ways that you can redirect assembly #1 to a different version of assembly #2. However, you would only do this if you knew that the version of assembly #2 that you are redirecting assembly #1 to use is compatible with the version of assembly #2 that assembly #1 was built with.

Note

Each application may have a configuration file where a developer or administrator can specify such things as the assembly binding settings, which affect how the CLR resolves references to assemblies. You can use these assembly binding settings to redirect all references to one version of an assembly to a different version. You will learn more about configuration files in Chapter 3. The .NET Framework also supports publisher policies, which allow the publisher of a component to redirect references to one version of an assembly to a different version. You will learn more about publisher policies in Chapter 3. System administrators on a host machine can turn off publisher policies if they want to.


Side-by-Side Execution

Side-by-side execution is the ability of the CLR to run multiple versions of the same assembly simultaneously . The CLR can even run multiple versions of the same assembly in different AppDomains within a single process (for example, if a Web server is running multiple applications that use different versions of an assembly). Side-by-side execution is probably the most important element of the .NET solution to the versioning problem. It means that each application can run with the same version of the components that it was built and tested with. Components in .NET no longer have to maintain backward compatibility. Backward compatibilityor the lack of itwas the main cause of the DLL hell problems in Win32/COM.

COM's versioning scheme required you to make newer versions of a component backward compatible with older ones so that all the applications on a box could then share the same version of the component. Unfortunately, it was difficult and time consuming to maintain backward compatibility. As time went by, you simply had too many versions to maintain compatibility with, and it is easy for subtle differences to creep in. The .NET solution of having each application deploy the version of its components that it was built and tested with and then having the CLR support side-by-side executions of multiple versions of the same component should banish the problem of DLL hell forever.


Team-Fly    
Top
 


. Net and COM Interoperability Handbook
The .NET and COM Interoperability Handbook (Integrated .Net)
ISBN: 013046130X
EAN: 2147483647
Year: 2002
Pages: 119
Authors: Alan Gordon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net