GAC Phobia

I was recently forwarded a link to a posting entitled “Avoid the GAC”. I would like to offer some insights into points addressed by this article, to help clarify misconceptions you may have about the GAC, and its intended usage.

The article argues that the GAC is the managed equivalent of the Windows system32 directory, and suffers from the same DLL sharing problems of the past (e.g. “DLL hell”). While it is pointed out that the GAC does facilitate side-by-side storage of DLLs based on the assembly’s strong identity (something that did not exist with system32), the author reasons that side-by-side storage isn’t useful because if a new version is available that your app didn’t use, then the update was pointless. Publisher policy is mentioned as a way to force all apps to use the update, and the argument concludes by saying that this ends up in exactly the same place we started with—system32.

A large source of confusion in this topic stems from what publisher policy really is and what it is intended for. There are two very different scenarios for updating an existing assembly:

1) Critical service updates (e.g. security fixes). These are tactical fixes intended to address specific problems with some previously released version of the component. No new features are in the update, and it is expected that the code is highly compatible with the existing version.

2) New feature releases. It may be the case that the new code is mostly compatible with the old version of the assembly, but there are no guarantees.

In case (1), it is clearly desirable to affect all applications on the machine. Because of the critical nature of security fixes, users (and administrators) want to be assured that if they apply a given security fix, that all applications are patched. This is a far better story than trying to hunt down n different applications on the machine that may be using the buggy component, and patching them individually. In the post-slammer-virus world, it should be evident that central servicing for security updates is a critical piece of ensuring machines are protected against malicious code.

In case (2), we recommend that component authors do not ship publisher policy statements which affect all applications. Because the compatibility of the assembly cannot be guaranteed against older versions, consuming the update should be done on an opt-in basis, rather than by default. You can selectively choose to use a new update for a particular application by utilizing application configuration policy.

To be clear, I am not advocating putting everything in the GAC. In fact, there was a fair amount of work put into supporting the xcopy deployment model, because of its simplicity and ease of use. An xcopied application is generally easier to move from machine to machine; there’s no complex setup necessary to install such an application.

If a publisher policy assembly containing a critical security fix is installed onto a given machine, it will affect all applications that use the older version of the assembly regardless of whether or not that assembly was xcopy deployed or installed into the GAC. But where does the assembly with the actual fixed bits go? The policy will affect every application that used the old version, and clearly there needs to be a place where those applications can find the fixed bits. The GAC is the natural choice here.

These two scenarios are somewhat confused in the “avoid the GAC” article. The author claims that there is no point in providing a new version if it is not intended to affect all applications—it may as well not be side-by-side. This is not true. As mentioned above, for security fixes, it is definitely desirable to affect all applications. Even so, this does not imply that security fixes should not be side-by-side. Inevitably, there will be cases where a critical security fix will break some application, and users/administrators will need to revert to the old functionality. Side-by-side helps you here, because it allows you to selectively roll back a given application to use the older version, while still allowing all other applications to use the fixed bits. This is a conscious decision by the user/administrator to bypass the security fix because this is an exceptional circumstance.

For non-security fixes, policy should not be applied to maintain maximum application compatibility. This, however, does not mean that one should avoid the GAC; these points are orthogonal. The GAC is designed to store multiple versions of the same component simultaneously. Two different functional/feature versions can happily live side-by-side without interfering with each other. Critical service updates can also live side-by-side with the pre-serviced version, in order to allow selective rollback where necessary.

A final point I would like to mention is that although disk space certainly is cheap in today’s world, sharing assemblies in a single location is still a very commonly requested feature. Sharing the same physical disk space location allows other optimizations, such as reduced memory pressure from not having to load the same copy of the DLL from many different locations (I will resist the urge to dive into related topics like domain neutrality and native images).

In summary, the old problems of “DLL hell” via DLL-name thrashing in system32 do not exist for the GAC. In the world of side-by-side, there are new considerations of how and when to update a component. While xcopy is definitely an attractive way to deploy your application/assembly, it is not the only way to do so, and there are legitimate reasons to make your component globally visible on the machine. Side-by-side allows users to selectively roll back applications which policy may have redirected forward.