A friend asked me last week what is the optimal solution to deal with application version upgrades. He develops a complex product distributed across a few servers that includes a combination of managed and native code, both internal development and from external third party open source. More specifically the question was what is the best place to put his DLLs? GAC, local folders, System32, SxS? My answer to him led to this post.
Before going into where to put the DLLs it should be clear that managing your DLLs and knowing where in your solution you use which version of them is not a question of where you chose to put them but how well you have documented what you did. Your primary focus should be to improve the process of how you release new versions of your solution in a way that it will provide proper documentation of the deployment. The best way is to automate this using a good ALM (application lifecycle management) tool like Team System or other.
Now to the question of where to put DLLs. As a general rule all DLLs should be versioned (I’ll talk about the exceptions later) from the very beginning of every solution. Sadly this is not the case and it is not uncommon to that I meet solutions where all the DLLs have version 1.0.0.0 even after a few releases were done.
First let’s talk about managed code DLLs which is what most of us usually work with:
Managed DLLs can be placed in one of two places
· GAC: Placing DLLs in the GAC gives you the benefit of deploying a DLL once to a central location for all your applications to use it. The risk is that you night break existing applications if your new DLL changed its signature. This is why you MUST version your DLLs and in each release deploy a new version increment of the DLLs which changed since the last release.
· Application local folder (for win application this is the folder where the exe is and for web applications it is the bin folder): Putting DLLs in local folders means the DLL instance is used only by that specific application. The benefit is that there is no risk of breaking one application when deploying another. The cons are that you have multiple instances of your DLL from various development versions deployed throughout the solution. When using this method you can have different versions of the DLL in different applications without actually versioning the DLL itself (all have version 1.0.0.0). This is a very bad practice because unless you keep excellent track of what version of the DLL you use in which folder you will quickly lose track of what code is used where. You might get, for example, to a situation where you need to fix a production bug in one of the applications but you don't know what code you have deployed.
So what is the best deployment method for managed code? As a general rule using the GAC is better since it can hold multiple versions of the same DLL and each application will use a specific version. The problem is that in a large solution this will make the GAC crowded with DLLs and hard to manage. This is why I recommend using a combination of deployment to the GAC and to local folders. For DLLs that are shared across applications or might be shared in the future like infrastructure and BL the GAC is the right choice. For DLLs specific to one application like GUI DLLs I’ll use that application local folder. These DLLs are only used in one place and so we always have the latest version in that local folder. There is always a gray area of DLLs you are not sure about. In such cases, put them in the GAC.
An important note about using the GAC. It requires to maintain a well defined DLL naming convention or else you will very quickly lose track of which DLLs are yours and which are from other applications. I also recommend performing regular cleanups of the GAC, deleting from unused DLL versions. You can know what can be deleted through reports generated from your deployment documentation (did I mention already an ALM tool?).
With native code DLLs things are not as easy. They can be deployed in four ways:
· System32: DLLs put in c:\windows\system32 can be used by all applications on the machine. There is no way to version DLLs placed there and so such a DLL should be backward compatible or all the applications that use it should be updated if a braking change was made.
· Application local folder: This is the same as with for managed code DLLs. With native code the fact you don’t need the DLL to be versioned is a more significant benefit since native code DLLs are not versioned as easily as the managed ones.
· Side-by-Side Native Assembly Cache (SxS): Starting form Windows XP you can deploy native DLLs side-by-side with versioning. For those who are not familiar with this way of deployment of native code you can read about it here and here. The DLLs are installed to C:\Windows\WinSxS. Each DLL installed is also strongly singed and versioned using a manifest. You then have the ability to create policy files which tell each application exactly which version of the DLL to use. The benefits are the same as using the GAC for managed code. The cons are that this requires a higher level of understanding of the dynamic loader policies and a more complex deployment process to support SxS.
· GAC: You can load native DLLs into the GAC by “attaching” them to a managed DLL. You can read about this here. I have never used this method but I believe it suits cases when all references to that DLL are done from code you control. It might be problematic if you have a DLL that is loaded by both managed and native DLLs. Also the deployment here is more complex.
So what is the best practice? In an ideal world I will handle native code like managed one and will combine the versioned SxS deployment for shared DLLs and local application folders for the non-shared. The issue here is that the SxS is not as easy to use as the GAC. You can automate adding the manifest and the special deployment actions by creating an MSI through an automatic build process (again the ALM tool to the aid) but this still adds complexity. This is why I’m more flexible here and allow exceptions for my strict versioning policy. If you have many native DLL at the core of your solution use the WinSxS but if there are only a few native DLLs you need to share across applications and these DLLs are not changed often, consider what method suits best your specific case and either deploy it to multiple application folders or attache it to a managed DLL or maybe keep only a single instance of it in system32 folder.
This post is targeted mostly for server deployments. Client side applications have even more options. ClickOnce for example is a good choice. Static linking of all the client's DLLs to a single exe is an excellent way to bundle up a client application (see skype for example). This was a common method at the native code times and is less used in the .NET world but is still possible using ILMerge tool.
There are many methodologies of how to deploy versioned solutions. This post is my personal best practices and preferences. You can look at the Microsoft guide for .NET solution deployment here. I will be happy to hear about your deployment experiences.