Share via


Host and Add-ins --- sharing host assemblies

This is based on a private discussion with David Levine. But the scenario is generally applicable.

 

David has an application with the following deployment:

 

Application

            Common

            Plugin

                        Plugin1

                        Plugin2

 

The application loads one or more plugins at runtime. Each plugin is deployed to its own directory. At runtime, the application loads the plugin in its own separate appdomain for isolation, with the application base of the appdomain set to the plugin’s directory. Plugins use assemblies in its own directory, as well as shared assemblies residing in directory Common.

 

The question is, fusion will not probe anything outside of application base (minus GAC). So how do plugins locate the shared assemblies?

 

There are only limited ways to load assemblies outside of the application base.

  1. GAC
  2. Assembly.LoadFrom/Assembly.LoadFile
  3. Codebase hint

1. GAC

 

Given the deployment model, GAC isn’t possible.

 

2. Assembly.LoadFrom/LoadFile

 

Assembly.LoadFrom/LoadFile works. But it has two disadvantages:

  1. It puts the assembly into a different load context, which may not be desirable.
  2. It assumes the plugin knows the deployment layout of the host (specifically, where the host puts the shared assemblies), which may or may not be true.

3. Codebase hint

 

Codebase hint will only work for strongly named assemblies. But given a host/plugin design, this is probably not too much to ask.

 

3.1 App.config

 

Let’s assume no change to machine.config. We leave with the option of app.config.

 

The app.config is authored by the plugin developer. It can’t know where the application is installed at development time, which means the plugin developer can’t put codebase hint for the shared assemblies in app.config.

 

Fortunately the application knows where the shared assemblies are installed, and it controls how to activate the plugins. It is possible to do some trick to make 3) happen.

 

Upon installation, the application can prepare an app.config template, with codebase hint pointing to the right location for the shared assemblies. Before it creates the plug in appdomain, the host merges the template with the real app.config, and use the processed app.config to create the new domain. The plugin now automatically knows how to locate the shared assemblies, and those assemblies are loaded into the default load context.

 

3.2 Host.config

 

Another way to use codebase hint is host config. Host config is projected to every appdomain so there is no need to do the processing above. But host config has a drawback that codebase hint in host config won’t be honored unless there is a bingingRedirect and the bindingRedirect results in a different version. But we can still use some trick to make that happen.

 

We can build a special version of those shared assemblies. They have a version number of “major.minor.0.0”. All the plugins references the special version at development time. The released assemblies with the application have the real version number (like “major.minor.month.date”).

 

Upon installation, the application generates a host.config, with bindingRedirect of “major.minor.0.0” to “major.minor.month.date”, and codebase hint to the location of the deployed shared assemblies.

 

Now at runtime, without any work, the plugins can automatically resolve the shared assemblies in default load context.

Comments

  • Anonymous
    November 21, 2005
    Hello Junfeng,

    It's been a while since I've had time to get up here - it's been very busy for me. I see you've written quite a few blogs, all of which are very useful. Thanks and keep them coming.

    I looked for you at the PDC but unfortunately you did not attend. Was anyone from the fusion team there?

    Thanks for remembering our discussions. I choose to use option 3.1. This allows me a great of flexibility and and was fairly easy to implement. All assemblies are strong named, and at build and again at install-time I perform a dependency analysis to ensure consistency and to generate the app.config template.

    I did not want to use option 3.2 as it requires me to host the runtime myself rather then use the default loader, although this is an option I may pursue once we move our development over to the 2.0 framework (we still use v1.1). Hopefully, the problem of not honoring the codebase hint until a binding redirect has occurred will be resolved by then. Is this going to be changed?

    Also, I have the option of putting assemblies into the GAC. I wrote my own form of Click-Once, one that has the ability to perform post-deploy custom actions. I could easily add a custom action to install assemblies to the GAC. However, I still prefer to keep private assemblies private and not fill the GAC with them. I believe the GAC could become a great source of headaches if misused/overused, so I chose to avoid it. Also, not using the GAC makes uninstall a simple matter of deleting the entire directory tree.

    Regards,
    Dave


  • Anonymous
    November 23, 2005
    hi David,

    Good to see you again.

    I did not go to PDC. None of the fusion team did.

    The host.config change did not make it into v2.0 RTM. It is qualified as DCR and the windows was closed for DCR. Unfortunate but I guess it is not the end of world.
  • Anonymous
    November 28, 2005
    Hello Jungfeng,
    You recommended creating a 'merged' app.config file as one of the solutions which I liked most. But I have one question. I am working on an ASP.NET application and by dropping plugin files in a predifined location and making selection from the hosting web application I want to make the host be able to find plugin assemlies at runtime without restarting the running application. Can you clarify the process of creating a merged app.config file, web.config in my case. And if this is purely file stream operations won't this cause the host restart as web.config will cause this restart on its change. One more thing, are there any security considerations for being able to change the web.config from a web application.
    Thanks a lot.
    Genna.
  • Anonymous
    November 28, 2005
    Jungfeng,
    After rereading your post i realised that i had a completely different scenario in my mind when I posted my previous comment. You load plugins in their own appDomains and make them find the shared assemblies they need. My scenario is similar but i try to solve a different task: I want the app, web application in my case, to be able to find plugins at runtime. Each plugin has its own package.config that is used by the app to lacate the services that the plugin provides.
    The application is a critical one and adding a plugin should not affect the application (no restart). The user uses an Admin page to decide when the plugin is to be loaded.
    I cannot update web.config on plugin deployment at runtime as this will restart the app, is there any way to add directories at runtime where the app will search for dependent assemblies (plug-in dlls).
    Thanks
    Genna.
  • Anonymous
    November 28, 2005
    You have to design your app to

    1. know there is a change
    2. know how to pick up the change.

    There is AppDomain.AppendPrivateBinPath. But it is deprecated.

    In general it is not a good idea to change probing behavior. If you really want, you can alwyas use Assembly.LoadFrom.