Pass and execute delegate in separate AppDomain
Asked Answered
S

4

33

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

Steapsin answered 5/1, 2010 at 20:1 Comment(0)
S
50

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.

Spitz answered 5/1, 2010 at 22:9 Comment(3)
The CreateInstanceAndUnwrap method is throwing an error saying it cannot find the assembly as specified by (typeof(Runtime).Assembly.FullName). This is running on a web server for me and the format of the fullname is "myhostdll, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null".Neutralize
@Triynko, when creating the new AppDomain setup, try specifying the same bin folder as the master domain, or at least, use a location where the new domain can obtain the assemblies.Lautrec
In my situation the Outlook process has created two app domains, one is mine and the other is from a vendor I am integrating with. Since I'm not able to explicitly created one of the domains as a child of another is your code still feasible?Diazine
E
9

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.

Emanate answered 14/1, 2013 at 14:12 Comment(0)
F
6

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).

Funches answered 5/1, 2010 at 20:17 Comment(2)
Are you sure you can use any delegate? From what I can see, as mentioned in my post, you can only use a delegate of type 'CrossAppDomainDelegate'Spitz
Any delegate that returns void and has no parameters can be used, but the CrossAppDomainDelegate can be created by passing it a reference to your method. Example: otherDomain.DoCallBack(new CrossAppDomainDelegate(MyCallBack)).Tilla
C
3

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.

Cohabit answered 5/1, 2010 at 20:4 Comment(1)
Actually, I'm creating such service. And I want to execute each request in separate domain.Steapsin

© 2022 - 2024 — McMap. All rights reserved.