Question

Can an abstract class be used as the contract object between a 'Host' and a 'plugin'? The idea is that the plugin inherits the contract (we call it an adapter). We are also understanding that all participants in the framework must inherit MarshalByRefObject (MBRO). So, this is what we were thinking -

Host:

class Host : MarshalByRefObject
{
}

Contract:

public abstract class PluginAdapter : MarshalByRefObject
{
}

Plugin:

class myPlugin : PluginAdapter
{
}

All three exist in separate asm's. Our Host will create a new AppDomain for each plugin, and the PluginAdapter is created as follows:

{
    ObjectHandle instHandle = Activator.CreateInstance(
    newDomain, data.Assembly.FullName, data.EntryPoint.FullName);

    PluginAdapter adapter = (PluginAdapter)instHandle.Unwrap();
}

EDIT: where data is the concrete type of myPlugin.

We were wondering if this implementation of the framework would work. We have seen articles using an interface (IPlugin) for the plugin derivation, and a concrete class as the contract. Those articles would also say that an abstract class can be used, but no examples of that implementation given. Is it required that the contract be a concrete class?

EDIT: In this example by Richard Blewett - C# Reflection - he uses a much simpler implementation:

Contract:

public interface IPlugIn  
{  
    // do stuff  
}

Plugin:

public class PlugIn : MarshalByRefObject, IPlugIn  
{  
}

Now, if using an abstract class as the contract, the Plugin cannot inherit both the contract and MBRO. What, then, becomes the best implementation for a scalable plugin framework. Should we go ahead and implement remoting even though, initially, we are developing for single-machine operation? This project is expected to become distributed across a network, possibly across the Internet as well. We simply have not implemented Tcp yet because we are trying to get the basics of a plugin framework fully understood and operational.

Does it make sense to implement Tcp remoting on a single machine using loopback?

Was it helpful?

Solution

Abstract classes are better choices for this, imho. Its primarily because interfaces are harder to version. This blog post describes the issue you might find yourself having down the road if you don't use base classes. This rule doesn't only apply to plugins, btw.

About your design...

Plugins shouldn't extend MBRO. You should use your Host (which should extend MBRO) to marshall all calls across to your plugins, including handling plugin events. Its very easy to unintentionally load a plugin DLL into your main appdomain if you attempt to pull them across and use their proxies.

For example, if the plugin returns an IEnumerable for one of its methods, it may return an implementation of IEnumerable that is defined in the plugin assembly. If that doesn't extend MBRO the main appdomain will have to load the plugin assembly.


I've uploaded three projects dealing with appdomains here:

http://cid-f8be9de57b85cc35.skydrive.live.com/self.aspx/Public/NET%20AppDomain%20Tests/appdomaintests.zip

One is using callbacks across appdomains, the second is cross-appdomain event handling, and the third is a plugin example.

In the plugin example, an application defines a plugin interface (its a demo, not best practices!) and a plugin host. The app loads a plugin assembly raw from disk and passes it over to the plugin appdomain via the plugin host proxy, where it is loaded. The plugin host then instantiates the plugin and uses it. But when the host returns a type defined in the plugin assembly back over into the application appdomain, the plugin assembly is loaded into the main application domain, rendering the whole plugin thing pointless.

Best thing to avoid this is provide an abstract base class for plugins that is not marked serializable and does not extend MBRO, and to only bring back primitives or sealed types you define back across the boundary from the plugin domain.

NOTE: the projects are all 4.0 RC. You'll need this or above to run them. Otherwise you'll have to edit the project files by hand or rebuild them to get them running in b2 or 2008.

OTHER TIPS

Provided "data.EntryPoint.FullName" is the full type name, the above code should work.

However, if you're trying to keep this type isolated in its own AppDomain, you should be careful here. By doing data.Assembly, you'll pull the assembly (and it's types) into your AppDomain, causing the types to be loaded in the executing AppDomain...

You may want to take a look at MAF (the Managed Addin Framework) which is an extensibility framework built into .NET for doing Addins. It is similar (and older) than MEF (Managed Extensibility Framework), but has more options as far as keeping plugins in their own app domain, among other things.

If data.EntryPoint.FullName refers to the concrete type myPlugin I don't see any reason why this wouldn't work (unless than having assembly loading issues in the other appdomain but that's a different issue).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top