I want to exceute some piece of code in separate AppDomain with delegate. How can I do this?
UPD1: some more details about my problem
My program processing some data (one iteration is: get some data from DB, evaluate it and create assemblies at runtime, execute dynamic assemblies and write results to DB).
Current solution: each iteration running in separate thread.
Better solution: each iteration running in separate AppDomain (to unload dynamic asseblies).
UPD2: All, thanks for answers.
I have found one for me in this thread:
Replacing Process.Start with AppDomains
Although you can make a call into a delegate which will be handled by a separate AppDomain, I personally have always used the 'CreateInstanceAndUnwrap' method which creates an object in the foreign app domain and returns a proxy to it.
For this to work your object has to inherit from MarshalByRefObject.
Here is an example:
public interface IRuntime
{
bool Run(RuntimesetupInfo setupInfo);
}
// The runtime class derives from MarshalByRefObject, so that a proxy can be returned
// across an AppDomain boundary.
public class Runtime : MarshalByRefObject, IRuntime
{
public bool Run(RuntimeSetupInfo setupInfo)
{
// your code here
}
}
// Sample code follows here to create the appdomain, set startup params
// for the appdomain, create an object in it, and execute a method
try
{
// Construct and initialize settings for a second AppDomain.
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
// Create the child AppDomain used for the service tool at runtime.
childDomain = AppDomain.CreateDomain(
"Your Child AppDomain", null, domainSetup);
// Create an instance of the runtime in the second AppDomain.
// A proxy to the object is returned.
IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName);
// start the runtime. call will marshal into the child runtime appdomain
return runtime.Run(setupInfo);
}
finally
{
// runtime has exited, finish off by unloading the runtime appdomain
if(childDomain != null) AppDomain.Unload(childDomain);
}
In the above sample, it is coded to execute a 'Run' method passing in some setup information, and completion of the Run method is determined to indicate that all code in the child AppDomain has completed running, so we have a finally block that ensures the AppDomain is unloaded.
You often may want to be careful in which types you place in which assemblies - you may want to use an interface and place it in a separate assembly that both the caller (our code that sets up the appdomain, and calls into it) and the implementer (the Runtime class) are dependent on. This IIRC allows the parent AppDomain to only load the assembly that contains the interface, while the child appdomain will load both the assembly that contains Runtime and it's dependency (the IRuntime assembly). Any user defined types that are used by the IRuntime interface (e.g. our RuntimeSetupInfo class) should usually also be placed in the same assembly as IRuntime. Also, be careful of how you define these user defined types - if they are data transfer objects (as RuntimeSetupInfo probably is), you should probably mark them with the [serializable] attribute - so that a copy of the object is passed (serialized from the parent appdomain to the child). You want to avoid calls being marshalled from one appdomain to another since this is pretty slow. Passing DTOs by value (serialization) means accessing values on the DTO doesn't incur a cross-apartment call (since the child appdomain has it's own copy of the original). Of course, this also means that value changes are not reflected in the parent appdomain's original DTO.
As is coded in the example, the parent appdomain will actually end up loading both the IRuntime and Runtime assemblys but that is because in the call to CreateInstanceAndUnwrap I am using typeof(Runtime) to get the assembly name and fully qualified type name. You could instead hardcode or retrieve these strings from a file - which would decouple the dependency.
There also is a method on AppDomain named 'DoCallBack' which looks like it allows calling a delegate in a foreign AppDomain. However, the delegate type that it takes is of type 'CrossAppDomainDelegate'. The definition of which is:
public delegate void CrossAppDomainDelegate()
So, it won't allow you to pass any data into it. And, since I've never used it, I can't tell you if there are any particular gotchas.
Also, I'd recommend looking into the LoaderOptimization property. What you set this to, can have a significant affect on performance, since some settings of this property force the new appdomain to load separate copies of all assemblies (and JIT them etc.) even if (IIRC) the assembly is in the GAC (i.e. this includes CLR assemblies). This can give you horrible performance if you use a large # of assemblies from your child appdomain. For e.g., I've used WPF from child appdomains which caused huge startup delays for my app until I setup a more appropriate load policy.
In order to execute a delegate on another AppDomain you can use System.AppDomain.DoCallBack(). The linked MSDN page has an excellent example. Note that You can only use delegates of type CrossAppDomainDelegate.
You need to read up on .NET Remoting and specifically on Remote Objects as these are all you can pass through AppDomains.
The long and short of it is that your object is either passed by value or by reference (via a proxy).
By value requires that your object be Serializable. Delegates are not serializable afaik. That means that this is not a good route to follow.
By reference requires that you inherit from MarshalByRefObject. This way, the remoting infrastructure can create the proxy. However, it also means that your delegate will be executed on the machine that creates it - not on the client app domain.
All in all, it's gonna be tricky. You might want to consider making your delegates full fledged serializable objects so that they can be easily moved around with remoting (and will work well with other technologies).
This doesn't answer your question directly but perhaps it would be better to create a WCF service or web service in the other AppDomain to preserve isolation. I don't know your particular situation but isolated architectural design is almost always the right way to go.
Related
My app allows plugins, I have a Core class (MarshalByRefObj) that plugins must inherit and this class offers various functionality. Now my question is, when this class is instantiated on main app domain and passed to the plugin in different app domain, what would be the benefit of using delegates in such scenario:
public class Core : MarshalByRefObject
{
public void DoSomething()
{
MyMainApp.Delegate1("SomeMethod", "Test");
}
}
So as you can see, my core class calls a delegate method on MyMainApp. I could as well just do MyMainApp.SomeMethod("test") instead.
However in many examples online about how remoting and plugin system works, everyone seems to be using delegates. Is there any specific reason for that? Could someone give me a more practical example of why?
Most of the time the controls in a user interface are created by the main thread unless you intentionally create them in another thread. Here is the important bit: ONLY the thread which created the control can access that control.
If you call DoSomething directly, and code in DoSomething wants to interact with a UI control, it will not be allowed and you will get an exception. MyMainApp.Delegate1("DoSomething" is equivalent to: Please execute the specified method on the main thread. Now it can access UI controls.
There are other reasons too but that is the most important bit to remember. See MSDN for more.
One of the benefits would be, that the information passed to the MyMainApp.Delegate1 is serialized for transport from the plugin appdomain to the main-appdomain. The Delegate1 method will execute the DoSomething in the main domain. They don't share memory (so no directly access to object instances is possible). So you can dynamically run methods on another appdomains. And if it's done via reflection, a plugin might be able to run unlisted methods.
I'd rather not use this type of construction, because there is no compile-time check on calling methods. I'd rather use interfaces that are in satelite assemblies. (to prevent the main-appdomain gets a reference to/loading the plugin assembly, so it can't be unloaded anymore)
The other thing:
If you call MyMainApp.SomeMethod("test") directly. This implies that the plugin must know the definition of the plugin loader. Meaning that you get a tight coupling (from the plugin) to the 'parent' application(s version). Which makes the whole plugin structure 'useless'. You could fix that by implementing a ISupportSomeMethod interface on the MyMainApp which is defined in a satelite assembly that is used by both the mainapp en the plugin. If your MyMainApp doesn't implement the ISupportSomeMethod interface, the plugin isn't compatible with that program. This way your MyMainApp can support multiple plugin structures.
In this case you prefer an event structure. Because the child object wants to trigger a method of it's parent. Too bad cross domain event calls are not useful, because your main module will load the assembly and it can't be unloaded. You could write a proxi class for that.
I'm trying to find a way to update an Asp.net web api (.Net framework 4.5) at runtime (without recycling the main appdomain) by adding new ApiController (downloaded by another service).
I tried to use Mef and was able to load the new ApiController in the current appdomain, but I got stuck when trying to update an existing plugin (the assembly is already added to the appdomain, so I can't add the new one).
So I decided to load the plugin containing the ApiController in a separate appdomain and use MarshalByRefObject to load it from the main appdomain but it turns out that ApiController cannot be serialized.
Do you know how I could serialize it?
Do you know an alternative?
Edit:
I managed to load different versions of an assembly (in the same appdomain) if the assembly is signed, but it doesn't match my requirements.
I haven't used MEF (because it is as easy to implement its functionality from scratch, in contradiction to MAF), but this way I have some experience with bare AppDomains.
It is hard to tell much without seeing your code, but from what you wrote, it seems to me that you are confusing some things.
As you probably know and you already pointed out too, you can't actually update an already loaded assembly. Loading another version of it (having a different signature) means that you have two different assemblies loaded. The types within them will have different strong names. You could actually handle this if you want. The only way to unload an assembly is to unload the appdomain that contains it.
My problem is with this sentence:
... load the plugin containing the ApiController in a separate appdomain
and use MarshalByRefObject to load it from the main appdomain
Type (class) definition+code and instance data are two different things. Loading an assembly into an appdomain means you are loading type definition and code. Serialization comes into view when you want to transfer instance data across appdomain borders. You can't load type definition and code from an other appdomain as you wrote (actually you could but I doubt you need to). To be able to transfer instance data both sides need to have knowledge about the type definition of the instance being transferred. The serialization and the transferred in this case is managed by the .net remoting runtime.
You have two choices: either move all the instance data and have it serialized all the time or you choose MarshalByObjRef way as you said you did. Let's stay with this. To be able to work with an instance in an other appdomain, you will need to instantiate the type in the other appdomain using the activator (you can't use the new operator in this case), and get a reference to it which will be a proxy based on the type you know (that can be an interface or a base class too, not only the exact type). Reflection is somewhat limited in such a situation, even less is prepared asp.net to figure out methods of a remote object - but you could help it with proper interfaces.
So, let's imagine you have created an instance of the controller in the other appdomain, and you have a remoting reference on it assignable to an interface type that defines all methods you need to expose to asp.net. Now serialization will come into view when are trying to access the members of the controller class. Each method parameter and method return type needs to be serializable. But not the class itself, as it is a MashalByObjRefdescendant and will not be mashalled as an instance. And MashalByObjRef has nothing to do with how you are loading the assembly into the appdomain.
But wait! Both MarshalByObjRef and ApiController are abstract classes. How do you want to derive your actual controller class from both? You can't. Thus I don't think you can directly use apicontrollers from an other appdomain directly.
I could imagine two things:
1) Stay with loading the new signed version into the same assembly and make customize the routing mechanism to direct requests to the latest version (might not be still valid, but could be still a good starting point: https://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/).
Of course, on restart, you should load only the latest one if you don't need to have multiple versions in parallel.
2) Make a slightly complex infrastructure:
define an interface for the controller logic
create the apicontroller versionless and logicless, but capable of creating and unloading appdomains, loading assemblies into them, keep reference to the instances implementing the interface from above created in them, and directing the requests to those
be aware that you won't be able to pass some things (like controller context) to the logic in the other appdomain, you will have to extract what you need or recreate on the other side
this way you can have the logic MarshalByObjRef descendant in the "remote" appdomain and your controller ApiController descendant in the main appdomain.
I would create an interim abstract class extending ApiController with the capability of handling the above separation on its own. The rest of the application wouldn't be aware of this.
be aware of the lifetime services involved in remoting, which you can handle either by using a sponsor or overriding some methods of MarshalByObjRef.
Neither is simple approach, you will be facing some further challenges...
I have an application where I'm using a high number of CSharpScript instances to control the application. The problem I'm facing is that the memory usage depends heavily on what type of global object I'm using. If the global type is defined in my application assembly (huge assembly) the memory usage rises by about ~100MB for every compiled script. If I instead put the global type in a separate assembly I find the memory usage to rise by ~10MB for each script which is still a lot for the amount of scripts I'm using.
Is there any way to limit this memory usage when using the CSharpScript API?
What I believe is happening here is that your script directly references an object defined in the main application. Since the script is compiled in to a separate AppDomain, this causes that AppDomain to pull in its own local copy of things from the AppDomain of the main application. Since you have 100s of scripts, each in their own AppDomain your main assembly gets loaded 100s of times.
One way to avoid this is to have any interfaces between the scripts and the application pass through a "shim" object, which is a small class, defined in its own assembly, that can "serialize" data across that AppDomain boundary. That Shim class should inherit from MarshalByReferenceObject. This can be tricky and complex, because the shim can't pass any of the classes defined in the application, or it will "suck in" the main assembly just like before. Everything must be passed as classes defined in .NET.
The MarshalByReferenceObject as a base class allows the shim to cross the domain boundary without bringing in an entire copy of the assembly. More information can be found at
https://msdn.microsoft.com/en-us/library/system.marshalbyrefobject(v=vs.110).aspx#Remarks
I don't know what is your specific implementation but it might be possible for you to do this:
Instead of giving the data to your scripts using big heavy global object, you can let the script give your application only their logic:
instead of this csx file, with HeavyObject as global:
// script.csx:
HeavyObject.DoSomeWork();
you can write this csx, with no global param needed:
// betterscript.csx:
public class ScriptWork : IHaveWork
{
Work(HeavyType obj)
{
obj.DoSomeWork();
}
}
return new ScriptWork();
you need this interface in your app:
public interface IHaveWork
{
void Work(HeavyType obj);
}
and the script should run with references to your application.
You would receive from your script an instance of IHaveWork, and then you should call Work within your application.
In my situation there are three components: Consumer class, IExposedIface interface and Exposed class implementing IExposedIface. Both Consumer and Exposed are linked statically with IExposedIface, but Consumer bears no compile-time reference to Exposed.
I am trying to come up with a scheme which would allow Consumer loading different versions of Exposed at runtime (depending on input data - let's say each input document carries an information about which version of Exposed should be used to process it). To achieve this, I started studying AppDomains and now I have a basic version working.
So far it seems to me there are two options when it comes to providing IExposedIface assembly to Exposed assembly.
Having IExposedIface.dll only in Consumer's bin directory and handling AppDomain.AssemblyResolve event for the AppDomain in which I am creating an instance of Exposed
Having IExposedIface.dll both in Consumer's bin directory as well as aside each Exposed.dll.
Now consider that I build Exposed against this IExposedIface:
public interface IExposedIface
{
string SaySomething();
}
and I build Consumer against this IExposedIface:
public interface IExposedIface
{
string SaySomething();
string SaySomethingDifferent();
}
In the first case, the exception
Exception: Method 'SaySomethingDifferent' in type 'Exposed.Exposed' from
assembly 'Exposed, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
is thrown in the moment I call appDomain.CreateInstanceAndUnwrap(...) to create an instance of Exposed in the freshly created AppDomain.
That looks reasonable to me.
But in the second case, appDomain.CreateInstanceAndUnwrap(...) goes through just fine and I can without problems call 'SaySomething()' method on the retrieved object. An exception
The method 'SaySomethingDifferent' was not found on the interface/type 'IExposedIface.IExposedIface, IExposedIface, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null'.
is only thrown when I actually call SaySomethingDifferent() in Consumer.
I was quite surprised that in this second case CLR had let me go so far...Could someone explain why this is possible?
Case #1 means that Exposed.dll is binding against the wrong version of IExposedIface.dll - the metadata loader is able to detect this when loading the assemblies because it finds an unimplemented interface method.
Case #2 (probably) means that you have the correct version of each IExposedIface.dll besides each Exposed.dll so each assembly can load within its own AppDomain. However AppDomain A has a different interface than AppDomain B which is only a problem when the call actually crosses the AppDomain border.
I'd suggest not trying those binary compatibility games and rather do proper versioning (ie. create a new interface with the new methods, inheriting from the old interface, so the new version of IExposedIface.dll is really backwards compatible). Anything else is really hard to debug because you can accidently end up loading both versions of IExposedIface.dll if they are reachable for windows, and then you have two versions of a Type in the AppDomain causing no end of trouble ;)
Is it possible to hold a reference to an Assembly from another appdomain without having that assembly loaded into the current appdomain?
I'm working on fixing a memory leak in a Windows Service that dynamically generates Assemblies and runs the dynamically generated code. The problem is the generated Assemblies are loaded into the Current app domain and can never be unloaded.
There is a method in one of the Windows Service libraries that has the follow signature:
public Assembly CreateMethod(ObservableCollection<Field> sourceFields, Field destinationField)
This method creates the code for the assembly and loads it with the CSScript library LoadMethod function:
result = CSScript.LoadMethod(scriptFunction.ToString());
Later this Assembly reference from CreateMethod is used to run a function inside the generated assembly.
public object Run(Field destinationField, ObservableCollection<LinkField> sourceLinkFields, DataRow mainRow, Assembly script) {
...
var method = script.GetStaticMethodWithArgs("*.a" + Id.ToString().Replace("-", String.Empty), argumentTypes.ToArray());
return method(arguments.ToArray());
}
I'm wondering if it is possible to load the dynamically generated assembly into another app domain and run them through some type of proxy without having it loaded into the current app domain.
Edit:
I want to know if I can use an Assembly class reference in one AppDomain when the assembly is loaded in another AppDomain. Looking at the MSDN documentation they show how to use MarshalByRefObject. Basically I am trying to avoid changing the signature to my CreateMethod function, however I may need to change it to return MarshalByRefObject if this is not possible.
Update:
I ended up putting the call to CSScript.LoadMethod in the other app domain where I keep a Dictionary I then made CreateMethod return a Guid instead of an Assembly and then I pass this Guid around until the Run call. The Run call now takes a Guid as an argument instead of an Assembly. Inside the Run call I pass the Guid to the other app domain, run the method, and return the result object through a class that inherits MarshalByRefObject.
If you don't want the dynamic assembly in your main AppDomain, you have to move CreateMethod to another AppDomain, because as soon as you have an instance of Assembly, it's been loaded. In other words, no it is not possible to hold a reference to an assembly in another application domain, only to call into that assembly across application domains.
Without changing the signature and a bunch of your code, it seems like you need to move the minimum amount: 1) assembly creation and 2) Run. Then have the implementation of Run marshall the results.
As far as CreateMethod I think you want a method in the other assembly to "wrap" CreateMethod and return some sort of token that can be passed to Run. It's almost like changing the signature in a way...
This is one of the major features of an AppDomain! Just go look at the documentation