Advanced Topic: Deploying Network Solutions to Be Cached Locally
This chapter began by noting that VSTO supports two main deployment scenarios: Local install ensures that customizations always work even when the user is not connected to the network, and network installs ensure that the user always has the latest version. It would be nice to have the best of both worlds: the latest version whenever you are online plus the ability to work offline.
If your users need to be able to always run the latest version of the customization while online or offline and also want to have one centralized point at which the customization can be updated, there are two principle techniques for doing so: Deploy the customization to a Web server, and deploy it to an IntelliMirror shared directory.
IntelliMirror Versus Web Caching
If the customization is deployed to a Web server, the Visual Studio runtime loader keeps a local copy of the assembly and configuration file in the Internet Explorer cache so that the customization is available when Internet Explorer is offline. Similarly, a locally cached IntelliMirror share makes the network customization seamlessly available even when the network share is not available.
All other things being equal, the IntelliMirror technique is to be preferred over the Web server technique for several reasons. For example, suppose you deploy your customization assembly to http://accounting, a local intranet Web server. A user runs the customized document, which downloads the customization assembly from the intranet site and caches a copy in the Internet Explorer cache. The user then unplugs his laptop from the wall, heads to the local library, and connects to the library's free wireless networking service. Now when the user tries to run the customized document, the .NET Framework will not load the customization assembly out of the Internet Explorer cache because Internet Explorer believes that it is connected to the network. Instead, the .NET Framework attempts to connect to the intranet server, fails, and prompts the user to go offline to use the locally cached copy. The user is then faced with the unfortunate choice of either not running the customization or putting Internet Explorer into offline mode, negating the benefits of having wireless Internet access.
Also, because the Web server caching scenario puts the customization assembly into the Internet Explorer cache, anything that causes the cache to be cleared destroys the cached customization assembly along with everything else. Many users frequently clear their Web caches when the caches get too large, and it is very easy to accidentally delete a cached customization assembly when you do not intend to.
Finally, a further inconvenience of the Web caching scenario is that all customizations must have a configuration file associated with them for the offline scenario to work. The next section discusses why.
Therefore, all other things being equal, if you want a hybrid online/offline scenario, the IntelliMirror technique is the preferred one. IntelliMirror shares pay no attention to the state of the Internet Explorer cache or online status.
Why Do We Need a Configuration File?
One of the goals explicitly stated earlier was to be able to have code live up on a Web server, so that it was always up-to-date, and yet be able to also access the code when the machine is disconnected. To achieve this goal, the first time the remote code is run, it is downloaded into a local cache. If you run the code again while connected, VSTO checks to ensure that the latest version is downloaded; if offline, VSTO runs the cached code.
Consider this scenario: Suppose your customization assembly is on a Web server along with a configuration file. The customization assembly uses version 220.127.116.11 of a strong-named assembly containing some useful routines you have written. The first time the user runs the application, the customization assembly and configuration file are downloaded and cached. The user goes offline, but the customization continues to work because the cached assembly and configuration file are available.
So far everything is good. Unfortunately, one day you discover a serious security hole in your library. You fix it, and release version 18.104.22.168 of the library. Every customization, however, still attempts to load the old code because the customization assembly was built against the old version. You would rather not go to the trouble of recompiling what might be hundreds or thousands of customizations against the new library; instead, you just update their server-side configuration files to say that the new version should be loaded when the old version is requested. While the user is offline, of course, they will still be running the insecure code, but there's nothing anyone can do about that; when they go back online and run the code, the new configuration file can be downloaded, the new library installed, and everyone is happy again.
That scenario is reason enough to always use configuration files; it is very handy to be able to easily change the assembly loading policy. But why not create a configuration file only when you find yourself in this unfortunate situation? Why do we require you to always create a configuration file if you want to be able to run server-side code while offline?
Well, suppose you did not create a configuration file; let's go through that scenario again. Your customization assembly is on the Web, without a configuration file, and uses buggy version 22.214.171.124 of your library assembly. The user runs the application for the first time. The loader finds the customization assembly and caches it, and determines that there is no configuration file on the server. Then the user goes offline.
You discover your security hole and roll out a configuration file pointing the loader to version 126.96.36.199 of your library assembly. The disconnected user knows none of this, and attempts to run the customization again.
Look at this from the point of view of the CLR assembly loader: It has been asked to load a file off of an unavailable Web server. It tries to find a local copy of the assembly, and succeeds. It tries to find a local copy of the configuration file, but fails. If you had cached a local copy of the configuration file, the CLR can assume that you meant for it to use that configuration file and that you were fine with using potentially out-of-date configuration information. But because there is no cached file, the CLR has to assume the worst: that there is in fact a new and important configuration file available that it cannot find.
Therefore, if you want to ensure that users must always be online and using the latest version of your server-side customization, you should not create a configuration file on the server. On the other hand, if you want to allow users to use cached assemblies and configuration files when your server is inaccessible, ensure that you have a configuration file on the server.
To add a configuration file to your project, right-click the project in the Solution Explorer and choose Add > New Item > Application Configuration File. Name the configuration file after the customization assembly filename; if your assembly is ExpenseReport.dll, name the configuration file ExpenseReport.dll.config.
<?xml version="1.0" encoding="utf-8" ?> <configuration> </configuration>
Configuration files do not get any more straightforward than that. To ensure that this file is copied up to the deployment server, make sure that the Build Action is set to Content in the Properties pane for the configuration file.