Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I instantiate a type as 'dynamic' from another AppDomain?

I'm trying to load a type from a different assembly (not known at build time) as 'dynamic' and execute a method on that type. My goal is to completely disconnect the 'plugin' from the parent application such that there is no requirement for any shared code or common interface type. The interface is implied by way of an expected method signature on the loaded type.

This works:

dynamic myObj = Assembly.Load("MyAssembly").CreateInstance("MyType");
myObj.Execute();

However this will load the type into the current AppDomain along with all its dependent assemblies. I want to modify this to allow me to do that same thing in a separate AppDomain.

This works but doesn't make use of the dynamic keyword, I need to know the explicit type that I am instantiating to be able to call the Execute method:

var appDomain = AppDomain.CreateDomain(domainName, evidence, setup);
var myObj = appDomain.CreateInstanceAndUnwrap(assembly, type);
typeof(IMyInterface).InvokeMember("Execute",  BindingFlags.InvokeMethod, null, myObj);

This is essentially my target case and I have been trying to get something like this working:

dynamic myObj = ad.CreateInstanceAndUnwrap(assembly, type);
myObj.Execute();

I keep ending up with a RuntimeBinderException with the message "'System.MarshalByRefObject' does not contain a definition for 'Execute'". This message makes sense, sure it doesn't contain a definition for 'Execute', but I know the type that I am instantiating does indeed contain an 'Execute' method. I imagine there's something going on here with the transparent proxy that is preventing this from working but I'm not sure what.

My actual class that I am trying to instantiate looks like this:

[Serializable]
public class MyClass : MarshalByRefObject {
  public void Execute() {
    // do something
  }
}

I have also tried this with a shared interface (not my primary goal, but I'm trying to figure this out first) so it would look like:

[Serializable]
public class MyClass : MarshalByRefObject, IPlugin {
  public void Execute() {
    // do something
  }
}

Where IPlugin is a known type in the parent application and the plugin has the appropriate reference at build time but this doesn't seem to work either.

I'm guessing at this point that it's not possible to load a type as dynamic across the AppDomain boundary.

Is there a way to actually get this to work?

like image 924
Chris Stavropoulos Avatar asked Jun 26 '12 18:06

Chris Stavropoulos


1 Answers

As leppie indicated, you'll have to implement the IDynamicMetaObjectProvider interface to wrap the proxy that's being returned to you, and then you can use make dynamic calls on that.

In your implementation, you'd want to take the wrapped proxy and forward all calls to the static ExecuteMessage method on the RemotingServices class, which will take your proxy, as well as an IMethodCallMessage interface implementation.

Note that implementing the IMethodCallMessage interface is not trivial. Also, you'd have to properly interpret the IMethodReturnMessage interface implementation to get the return value, ref and out parameters correctly (if any).

That said, it's generally a better idea to provide an assembly that contains only an interface for the client and server to assume; if a method is changed in any way on the server side, even though the client side uses dynamic, you'd still have to change the call site to accommodate the change. At least with the interface, you get some type of compile-time check, which is always preferred to a run-time error.

like image 200
casperOne Avatar answered Oct 16 '22 00:10

casperOne