Prevent All Dependencies From Loading

Topics: Bootstrappers & IoC
Sep 12, 2013 at 6:02 PM
Edited Sep 12, 2013 at 9:14 PM
I am investigating migrating application that works in two modes, as a standalone application and as a plugin that integrates into another program, to use CM as the MVVM framework. I have setup the application so that each mode uses a different bootstrapper to configure and start the CM framework correctly. However, I have run into an issue that I have been unable to resolve.

When the application is run in plugin mode, it is accessed through a class that implements a COM interface defined in external libraries that are provided to the .Net environment by the host application. This class in turn starts a different bootstrapper to initialize CM and the application in plugin mode. The interface libraries are referenced in my solution, but I have set "CopyLocal" to false and the libraries are not shipped with my application so that it can be used within different versions of the host. However, because there is a class in the assembly that implements the COM interfaces, the ViewLocator in CM attempts to load the external libraries even when the program is run in standalone mode.

Unfortuntately, I have been unable to find a way to remove the attempt to load the external libraries, which is necessary in order for the program to run in standalone mode.

Thanks!
Sep 13, 2013 at 7:13 AM
What kind of container are you using? CM per-se does not perform dynamic loading of assemblies, it is the actual IoC that performs this task.
Moreover, are your plugins leaving in different AppDomains? If not, having multiple bootstrappers is meaningless, since many extention points are defined as static delegates, so there is an high chance that the last bootstrapper to be created will replace previous registrations (IoC too).

To have CM as part of a plugin system, you need to ensure that a unique bootstrapper is created. I would first create a module whose sole purpose is configuring and starting up a bootstrapper, then all plugins should simply use CM in the usual way (see this post for a similar discussion).
Sep 16, 2013 at 3:32 PM
Edited Sep 16, 2013 at 3:33 PM
As I was writing my original post, I realized the situation is a bit more complex and confusing than I described.

We have a series of tools that can be run as either a standalone tool or integrated into a larger framework. Each tool is distributed as a Windows executable that (also) implements an out-of-process COM server that interacts with the framework application (the COM client in this case). Because the framework application interacts with COM servers, it has no knowledge of AppDomains, etc. Multiple instances of the same tool can be used within the framework and when this happens all instances are created in the same AppDomain, which does cause issues with static objects, etc.

Each tool can be started in one of two ways, either by running the tool directly (we call this standalone mode) or by being created in the framework (we call this plugin mode). When the tool is started in standalone mode, it runs a normal CM bootstrapper with minimal customization. When the tool is started in plugin mode, it runs a slightly more customized bootstrapper that inherits from the base bootstrapper, but sets the useApplication parameter to false.

The COM server interface (let's call this IFramework) has essentially four methods:
  1. A construct(IHost host, ...) method that instances the tool and passes three objects to facilitate communication with the framework. In this method, I instantiate the customized bootstrapper and call the start() method on the bootstrapper.
  2. A show() method to show a user interface if necessary. In this method, I use IoC to get my customized WindowManager and then use it to display the view associated with the root viewmodel. In almost all cases, at some point this method requires use of the three objects passed in the construct() method.
  3. A end() method to perform cleanup and whatnot.
  4. (Actually a couple of) event() methods to faciliate responding to events from the framework. These methods rely on the objects passed by the the construct() method.
My current problem is that since there is a class that implements the IFramework interface, the ViewLocator is trying to load the IFramework.Interop.dll file that contains the IFramework interface. Because our tool can run many versions of the host framework, each of which has it's own version of the IFramework.Interop.dll file, and the dll is not needed when the tool is run in standalone mode, we don't ship our tool with a copy of the dll. When our tool is run in standalone mode, the ViewLocator then crashes when it unable to locate/load the dll as expected.

In the larger context, it is likely the static methods in CM will cause additional headaches... In the past we have looked at creating a separate AppDomain in the construct() method, but we were never able to identify a clever way to respond to the event methods and share the interaction objects across the AppDomain boundary. Any thoughts here?

Thanks!
Sep 17, 2013 at 7:40 AM
Since your plugin are instanced inside the same AppDomain, can't your main application share the Bootstrapper?
What I mean is that as long as every plugin is aware of the fact that it is not running as a stand-alone program, it could decide to slightly modify its initialization code (as an example, instead of configuring the IoC, just retrieve the instance of the container that has been registered by the main application).

The idea behind the boostrapper is that it is the starting point to configure the CM environment, unless your bootstrapper plugin configurations are conflicting (e.g. different IoCs are used for different plugins), sharing the bootstrapper seems to be the best option.